@flight-framework/cli 0.0.16 → 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/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/bin.js +524 -13
- package/dist/bin.js.map +1 -1
- package/dist/index.js +524 -13
- package/dist/index.js.map +1 -1
- package/package.json +51 -51
- package/templates/use-cases/api/README.md +41 -0
- package/templates/use-cases/api/package.json.template +14 -0
- package/templates/use-cases/api/src/routes/api/health.get.ts.template +3 -0
- package/templates/use-cases/blog/README.md +47 -0
- package/templates/use-cases/blog/flight.config.ts.template +11 -0
- package/templates/use-cases/blog/package.json.template +15 -0
- package/templates/use-cases/blog/src/routes/blog/[slug].page.tsx.template +23 -0
- package/templates/use-cases/blog/src/routes/index.page.tsx.template +9 -0
- package/templates/use-cases/docs/README.md +49 -0
- package/templates/use-cases/docs/package.json.template +15 -0
- package/templates/use-cases/docs/src/content/index.md.template +16 -0
- package/templates/use-cases/ecommerce/README.md +32 -0
- package/templates/use-cases/ecommerce/package.json.template +16 -0
- package/templates/use-cases/ecommerce/src/routes/index.page.tsx.template +9 -0
- package/templates/use-cases/saas/README.md +34 -0
- package/templates/use-cases/saas/package.json.template +15 -0
- package/templates/use-cases/saas/src/routes/index.page.tsx.template +9 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Flight Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -123,6 +123,76 @@ flight create my-app --ui solid --features forms,auth,i18n
|
|
|
123
123
|
| `--no-git` | Skip git initialization |
|
|
124
124
|
| `--install` | Install dependencies (default: true) |
|
|
125
125
|
| `--no-install` | Skip dependency installation |
|
|
126
|
+
| `--raw` | Create raw project (100% Web Standards, zero dependencies) |
|
|
127
|
+
| `--empty` | Create empty project (just package.json) |
|
|
128
|
+
| `--minimal` | Create minimal project (single server file) |
|
|
129
|
+
|
|
130
|
+
### Toolbox Mode (Zero Lock-in)
|
|
131
|
+
|
|
132
|
+
Start with the minimum and add what you need:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Raw project - 100% Web Standards, ZERO dependencies
|
|
136
|
+
# Works on Bun, Deno, Node 22+, Cloudflare Workers
|
|
137
|
+
flight create my-app --raw
|
|
138
|
+
|
|
139
|
+
# Empty project - just package.json
|
|
140
|
+
flight create my-app --empty
|
|
141
|
+
|
|
142
|
+
# Minimal project - single server file with Flight
|
|
143
|
+
flight create my-app --minimal
|
|
144
|
+
|
|
145
|
+
# Then add packages as needed
|
|
146
|
+
cd my-app
|
|
147
|
+
flight add http
|
|
148
|
+
flight add db
|
|
149
|
+
flight add cache
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The `--raw` mode generates code with **zero Flight dependencies**. You can run it on any runtime and add Flight packages later if you want.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Adding Packages
|
|
157
|
+
|
|
158
|
+
Add Flight packages to an existing project:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
flight add [package]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Available Packages
|
|
165
|
+
|
|
166
|
+
| Package | Description |
|
|
167
|
+
|---------|-------------|
|
|
168
|
+
| `http` | HTTP server with routing and middleware |
|
|
169
|
+
| `core` | File-based routing and configuration |
|
|
170
|
+
| `db` | Database abstraction layer |
|
|
171
|
+
| `cache` | Caching with multiple adapters |
|
|
172
|
+
| `auth` | Authentication adapters |
|
|
173
|
+
| `forms` | Type-safe form handling |
|
|
174
|
+
| `i18n` | Internationalization |
|
|
175
|
+
| `seo` | SEO utilities |
|
|
176
|
+
| `image` | Image optimization |
|
|
177
|
+
| `email` | Email sending |
|
|
178
|
+
| `realtime` | WebSocket and real-time features |
|
|
179
|
+
| `helpers` | Optional helper utilities |
|
|
180
|
+
|
|
181
|
+
### Examples
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Add HTTP server
|
|
185
|
+
flight add http
|
|
186
|
+
|
|
187
|
+
# Add database support
|
|
188
|
+
flight add db
|
|
189
|
+
|
|
190
|
+
# Add authentication
|
|
191
|
+
flight add auth
|
|
192
|
+
|
|
193
|
+
# See all packages
|
|
194
|
+
flight add
|
|
195
|
+
```
|
|
126
196
|
|
|
127
197
|
---
|
|
128
198
|
|
package/dist/bin.js
CHANGED
|
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
10
|
import { cac } from "cac";
|
|
11
|
-
import
|
|
11
|
+
import pc6 from "picocolors";
|
|
12
12
|
|
|
13
13
|
// src/version.ts
|
|
14
14
|
var VERSION = "0.0.1";
|
|
@@ -61,6 +61,21 @@ async function createCommand(name, options) {
|
|
|
61
61
|
console.log(pc.red("Project creation cancelled."));
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
|
+
if (options.raw) {
|
|
65
|
+
const projectPath2 = resolve(process.cwd(), projectName);
|
|
66
|
+
createRawProject(projectPath2, projectName, options);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (options.empty) {
|
|
70
|
+
const projectPath2 = resolve(process.cwd(), projectName);
|
|
71
|
+
createEmptyProject(projectPath2, projectName, options);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (options.minimal) {
|
|
75
|
+
const projectPath2 = resolve(process.cwd(), projectName);
|
|
76
|
+
createMinimalProject(projectPath2, projectName, options);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
64
79
|
if (!uiFramework) {
|
|
65
80
|
const response = await prompts({
|
|
66
81
|
type: "select",
|
|
@@ -75,6 +90,9 @@ async function createCommand(name, options) {
|
|
|
75
90
|
console.log(pc.red("Project creation cancelled."));
|
|
76
91
|
return;
|
|
77
92
|
}
|
|
93
|
+
let useCase = options.useCase;
|
|
94
|
+
const useCaseDir = join(TEMPLATES_DIR, "use-cases", useCase || "");
|
|
95
|
+
const isUseCase = useCase && existsSync(useCaseDir);
|
|
78
96
|
let bundler = options.bundler;
|
|
79
97
|
if (!bundler) {
|
|
80
98
|
const response = await prompts({
|
|
@@ -110,8 +128,12 @@ async function createCommand(name, options) {
|
|
|
110
128
|
Creating project in ${projectPath}...
|
|
111
129
|
`));
|
|
112
130
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
131
|
+
if (isUseCase && useCase) {
|
|
132
|
+
copyUseCaseTemplate(projectPath, useCase, projectName);
|
|
133
|
+
} else {
|
|
134
|
+
copyTemplate(projectPath, uiFramework, bundler, projectName);
|
|
135
|
+
}
|
|
136
|
+
console.log(pc.green("[OK]") + " Project structure created");
|
|
115
137
|
if (options.git) {
|
|
116
138
|
try {
|
|
117
139
|
execSync("git init", { cwd: projectPath, stdio: "ignore" });
|
|
@@ -179,6 +201,15 @@ function copyTemplate(projectPath, ui, bundler, projectName) {
|
|
|
179
201
|
unlinkSync(gitignoreSrc);
|
|
180
202
|
}
|
|
181
203
|
}
|
|
204
|
+
function copyUseCaseTemplate(projectPath, useCase, projectName) {
|
|
205
|
+
const useCaseDir = join(TEMPLATES_DIR, "use-cases", useCase);
|
|
206
|
+
mkdirSync(projectPath, { recursive: true });
|
|
207
|
+
const vars = {
|
|
208
|
+
"{{PROJECT_NAME}}": projectName,
|
|
209
|
+
"{{USE_CASE}}": useCase
|
|
210
|
+
};
|
|
211
|
+
copyDirWithTemplates(useCaseDir, projectPath, vars);
|
|
212
|
+
}
|
|
182
213
|
function copyDirWithTemplates(srcDir, destDir, vars) {
|
|
183
214
|
if (!existsSync(srcDir)) return;
|
|
184
215
|
const entries = readdirSync(srcDir, { withFileTypes: true });
|
|
@@ -205,6 +236,277 @@ function copyDirWithTemplates(srcDir, destDir, vars) {
|
|
|
205
236
|
}
|
|
206
237
|
}
|
|
207
238
|
}
|
|
239
|
+
function createRawProject(projectPath, projectName, options) {
|
|
240
|
+
mkdirSync(projectPath, { recursive: true });
|
|
241
|
+
const packageJson = {
|
|
242
|
+
name: projectName,
|
|
243
|
+
version: "0.0.1",
|
|
244
|
+
type: "module",
|
|
245
|
+
scripts: {
|
|
246
|
+
// Works with Bun, Node 22+, or Deno
|
|
247
|
+
"dev": "node --watch server.js",
|
|
248
|
+
"dev:bun": "bun --watch server.js",
|
|
249
|
+
"dev:deno": "deno run --allow-net server.js",
|
|
250
|
+
"start": "node server.js"
|
|
251
|
+
},
|
|
252
|
+
dependencies: {},
|
|
253
|
+
devDependencies: {}
|
|
254
|
+
};
|
|
255
|
+
writeFileSync(
|
|
256
|
+
join(projectPath, "package.json"),
|
|
257
|
+
JSON.stringify(packageJson, null, 2)
|
|
258
|
+
);
|
|
259
|
+
const serverCode = `/**
|
|
260
|
+
* ${projectName}
|
|
261
|
+
*
|
|
262
|
+
* 100% Web Standards server. Zero dependencies. Zero lock-in.
|
|
263
|
+
* Works on: Bun, Deno, Node 22+, Cloudflare Workers
|
|
264
|
+
*
|
|
265
|
+
* Run:
|
|
266
|
+
* bun server.js
|
|
267
|
+
* deno run --allow-net server.js
|
|
268
|
+
* node server.js (Node 22+)
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Handle incoming requests using Web Standard APIs
|
|
273
|
+
* @param {Request} request
|
|
274
|
+
* @returns {Response}
|
|
275
|
+
*/
|
|
276
|
+
function handleRequest(request) {
|
|
277
|
+
const url = new URL(request.url);
|
|
278
|
+
const method = request.method;
|
|
279
|
+
|
|
280
|
+
// Router
|
|
281
|
+
if (method === 'GET' && url.pathname === '/') {
|
|
282
|
+
return Response.json({
|
|
283
|
+
message: 'Hello World!',
|
|
284
|
+
runtime: detectRuntime(),
|
|
285
|
+
docs: 'https://flight.dev/docs/quickstart-http',
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (method === 'GET' && url.pathname === '/health') {
|
|
290
|
+
return Response.json({
|
|
291
|
+
status: 'ok',
|
|
292
|
+
timestamp: Date.now(),
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (method === 'GET' && url.pathname.startsWith('/api/')) {
|
|
297
|
+
return Response.json({
|
|
298
|
+
path: url.pathname,
|
|
299
|
+
query: Object.fromEntries(url.searchParams),
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 404
|
|
304
|
+
return Response.json(
|
|
305
|
+
{ error: 'Not Found', path: url.pathname },
|
|
306
|
+
{ status: 404 }
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Detect which runtime we're running on
|
|
312
|
+
*/
|
|
313
|
+
function detectRuntime() {
|
|
314
|
+
if (typeof Bun !== 'undefined') return 'bun';
|
|
315
|
+
if (typeof Deno !== 'undefined') return 'deno';
|
|
316
|
+
return 'node';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Export for different runtimes
|
|
320
|
+
const port = process.env.PORT || 3000;
|
|
321
|
+
|
|
322
|
+
// Bun / Cloudflare Workers style
|
|
323
|
+
export default {
|
|
324
|
+
port,
|
|
325
|
+
fetch: handleRequest,
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// Also start server for Node.js
|
|
329
|
+
if (detectRuntime() === 'node') {
|
|
330
|
+
const { serve } = await import('node:http');
|
|
331
|
+
serve({ port }, (req, res) => {
|
|
332
|
+
const url = 'http://localhost' + req.url;
|
|
333
|
+
const request = new Request(url, { method: req.method });
|
|
334
|
+
handleRequest(request).then(response => {
|
|
335
|
+
res.writeHead(response.status, Object.fromEntries(response.headers));
|
|
336
|
+
response.text().then(body => res.end(body));
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
console.log(\`Server running at http://localhost:\${port}\`);
|
|
340
|
+
}
|
|
341
|
+
`;
|
|
342
|
+
writeFileSync(join(projectPath, "server.js"), serverCode);
|
|
343
|
+
if (options.git) {
|
|
344
|
+
writeFileSync(
|
|
345
|
+
join(projectPath, ".gitignore"),
|
|
346
|
+
"node_modules\ndist\n.env\n.env.local\n"
|
|
347
|
+
);
|
|
348
|
+
try {
|
|
349
|
+
execSync("git init", { cwd: projectPath, stdio: "ignore" });
|
|
350
|
+
} catch {
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
console.log(`
|
|
354
|
+
${pc.green("[OK] Raw project created!")}
|
|
355
|
+
|
|
356
|
+
${pc.bold("ZERO dependencies. ZERO lock-in. 100% Web Standards.")}
|
|
357
|
+
|
|
358
|
+
${pc.cyan("Run with any runtime:")}
|
|
359
|
+
|
|
360
|
+
${pc.dim("# Bun (fastest)")}
|
|
361
|
+
bun server.js
|
|
362
|
+
|
|
363
|
+
${pc.dim("# Deno")}
|
|
364
|
+
deno run --allow-net server.js
|
|
365
|
+
|
|
366
|
+
${pc.dim("# Node.js 22+")}
|
|
367
|
+
node server.js
|
|
368
|
+
|
|
369
|
+
${pc.dim("Want to add Flight later? Just run:")}
|
|
370
|
+
flight add http ${pc.dim("# HTTP server with routing")}
|
|
371
|
+
flight add db ${pc.dim("# Database abstraction")}
|
|
372
|
+
flight add cache ${pc.dim("# Caching layer")}
|
|
373
|
+
|
|
374
|
+
${pc.dim("Path:")} ${projectPath}
|
|
375
|
+
`);
|
|
376
|
+
}
|
|
377
|
+
function createEmptyProject(projectPath, projectName, options) {
|
|
378
|
+
mkdirSync(projectPath, { recursive: true });
|
|
379
|
+
const packageJson = {
|
|
380
|
+
name: projectName,
|
|
381
|
+
version: "0.0.1",
|
|
382
|
+
type: "module",
|
|
383
|
+
scripts: {
|
|
384
|
+
dev: "node server.js"
|
|
385
|
+
},
|
|
386
|
+
dependencies: {},
|
|
387
|
+
devDependencies: {}
|
|
388
|
+
};
|
|
389
|
+
writeFileSync(
|
|
390
|
+
join(projectPath, "package.json"),
|
|
391
|
+
JSON.stringify(packageJson, null, 2)
|
|
392
|
+
);
|
|
393
|
+
if (options.git) {
|
|
394
|
+
writeFileSync(
|
|
395
|
+
join(projectPath, ".gitignore"),
|
|
396
|
+
"node_modules\ndist\n.env\n.env.local\n"
|
|
397
|
+
);
|
|
398
|
+
try {
|
|
399
|
+
execSync("git init", { cwd: projectPath, stdio: "ignore" });
|
|
400
|
+
} catch {
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
console.log(`
|
|
404
|
+
${pc.green("[OK] Empty project created!")}
|
|
405
|
+
|
|
406
|
+
${pc.cyan("Your project is a blank canvas.")} Add what you need:
|
|
407
|
+
|
|
408
|
+
${pc.dim("# HTTP server")}
|
|
409
|
+
npm install @flight-framework/http
|
|
410
|
+
|
|
411
|
+
${pc.dim("# Database")}
|
|
412
|
+
npm install @flight-framework/db
|
|
413
|
+
|
|
414
|
+
${pc.dim("# Cache")}
|
|
415
|
+
npm install @flight-framework/cache
|
|
416
|
+
|
|
417
|
+
${pc.dim("# Authentication")}
|
|
418
|
+
npm install @flight-framework/auth
|
|
419
|
+
|
|
420
|
+
${pc.dim("Path:")} ${projectPath}
|
|
421
|
+
`);
|
|
422
|
+
}
|
|
423
|
+
function createMinimalProject(projectPath, projectName, options) {
|
|
424
|
+
mkdirSync(projectPath, { recursive: true });
|
|
425
|
+
const packageJson = {
|
|
426
|
+
name: projectName,
|
|
427
|
+
version: "0.0.1",
|
|
428
|
+
type: "module",
|
|
429
|
+
scripts: {
|
|
430
|
+
dev: "node --watch server.js",
|
|
431
|
+
start: "node server.js"
|
|
432
|
+
},
|
|
433
|
+
dependencies: {
|
|
434
|
+
"@flight-framework/http": "^0.0.1"
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
writeFileSync(
|
|
438
|
+
join(projectPath, "package.json"),
|
|
439
|
+
JSON.stringify(packageJson, null, 2)
|
|
440
|
+
);
|
|
441
|
+
const serverCode = `/**
|
|
442
|
+
* ${projectName} - Built with Flight
|
|
443
|
+
*
|
|
444
|
+
* This is a minimal Flight server. Add more as you need.
|
|
445
|
+
* Run: npm run dev
|
|
446
|
+
*/
|
|
447
|
+
|
|
448
|
+
import { createServer } from '@flight-framework/http';
|
|
449
|
+
|
|
450
|
+
const app = createServer();
|
|
451
|
+
|
|
452
|
+
// Your routes
|
|
453
|
+
app.get('/', (c) => c.json({
|
|
454
|
+
message: 'Hello from Flight!',
|
|
455
|
+
docs: 'https://flight.dev/docs/quickstart-http'
|
|
456
|
+
}));
|
|
457
|
+
|
|
458
|
+
app.get('/health', (c) => c.json({
|
|
459
|
+
status: 'ok',
|
|
460
|
+
timestamp: Date.now()
|
|
461
|
+
}));
|
|
462
|
+
|
|
463
|
+
// Start server
|
|
464
|
+
const port = process.env.PORT || 3000;
|
|
465
|
+
console.log(\`Server running at http://localhost:\${port}\`);
|
|
466
|
+
|
|
467
|
+
export default { port, fetch: app.fetch };
|
|
468
|
+
`;
|
|
469
|
+
writeFileSync(join(projectPath, "server.js"), serverCode);
|
|
470
|
+
if (options.git) {
|
|
471
|
+
writeFileSync(
|
|
472
|
+
join(projectPath, ".gitignore"),
|
|
473
|
+
"node_modules\ndist\n.env\n.env.local\n"
|
|
474
|
+
);
|
|
475
|
+
try {
|
|
476
|
+
execSync("git init", { cwd: projectPath, stdio: "ignore" });
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (options.install) {
|
|
481
|
+
console.log(pc.dim("\\nInstalling dependencies...\\n"));
|
|
482
|
+
try {
|
|
483
|
+
const pm = detectPackageManager();
|
|
484
|
+
execSync(`${pm} install`, { cwd: projectPath, stdio: "inherit" });
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
console.log(`
|
|
489
|
+
${pc.green("[OK] Minimal project created!")}
|
|
490
|
+
|
|
491
|
+
${pc.cyan("Next steps:")}
|
|
492
|
+
|
|
493
|
+
${pc.dim("$")} cd ${projectName}
|
|
494
|
+
${pc.dim("$")} ${options.install ? "" : "npm install && "}npm run dev
|
|
495
|
+
|
|
496
|
+
${pc.dim("Add more features as needed:")}
|
|
497
|
+
|
|
498
|
+
${pc.dim("# File-based routing")}
|
|
499
|
+
npm install @flight-framework/core
|
|
500
|
+
|
|
501
|
+
${pc.dim("# Database")}
|
|
502
|
+
npm install @flight-framework/db pg
|
|
503
|
+
|
|
504
|
+
${pc.dim("# Caching")}
|
|
505
|
+
npm install @flight-framework/cache
|
|
506
|
+
|
|
507
|
+
${pc.cyan("Happy flying!")}
|
|
508
|
+
`);
|
|
509
|
+
}
|
|
208
510
|
|
|
209
511
|
// src/commands/dev.ts
|
|
210
512
|
import { resolve as resolve2, join as join2 } from "path";
|
|
@@ -1294,25 +1596,234 @@ async function typesGenerateCommand(options = {}) {
|
|
|
1294
1596
|
}
|
|
1295
1597
|
}
|
|
1296
1598
|
|
|
1599
|
+
// src/commands/add.ts
|
|
1600
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1601
|
+
import { join as join5 } from "path";
|
|
1602
|
+
import { execSync as execSync2 } from "child_process";
|
|
1603
|
+
import pc5 from "picocolors";
|
|
1604
|
+
var PACKAGES = {
|
|
1605
|
+
// Core
|
|
1606
|
+
"http": {
|
|
1607
|
+
name: "@flight-framework/http",
|
|
1608
|
+
description: "HTTP server with routing and middleware",
|
|
1609
|
+
category: "core"
|
|
1610
|
+
},
|
|
1611
|
+
"core": {
|
|
1612
|
+
name: "@flight-framework/core",
|
|
1613
|
+
description: "File-based routing and configuration",
|
|
1614
|
+
category: "core"
|
|
1615
|
+
},
|
|
1616
|
+
// Data
|
|
1617
|
+
"db": {
|
|
1618
|
+
name: "@flight-framework/db",
|
|
1619
|
+
description: "Database abstraction layer",
|
|
1620
|
+
category: "data",
|
|
1621
|
+
drivers: ["pg", "@libsql/client", "@neondatabase/serverless", "@supabase/supabase-js"]
|
|
1622
|
+
},
|
|
1623
|
+
"cache": {
|
|
1624
|
+
name: "@flight-framework/cache",
|
|
1625
|
+
description: "Caching with multiple adapters",
|
|
1626
|
+
category: "data"
|
|
1627
|
+
},
|
|
1628
|
+
// Auth
|
|
1629
|
+
"auth": {
|
|
1630
|
+
name: "@flight-framework/auth",
|
|
1631
|
+
description: "Authentication adapters",
|
|
1632
|
+
category: "auth"
|
|
1633
|
+
},
|
|
1634
|
+
// Frontend
|
|
1635
|
+
"forms": {
|
|
1636
|
+
name: "@flight-framework/forms",
|
|
1637
|
+
description: "Type-safe form handling",
|
|
1638
|
+
category: "frontend"
|
|
1639
|
+
},
|
|
1640
|
+
"i18n": {
|
|
1641
|
+
name: "@flight-framework/i18n",
|
|
1642
|
+
description: "Internationalization",
|
|
1643
|
+
category: "frontend"
|
|
1644
|
+
},
|
|
1645
|
+
"seo": {
|
|
1646
|
+
name: "@flight-framework/seo",
|
|
1647
|
+
description: "SEO utilities",
|
|
1648
|
+
category: "frontend"
|
|
1649
|
+
},
|
|
1650
|
+
"image": {
|
|
1651
|
+
name: "@flight-framework/image",
|
|
1652
|
+
description: "Image optimization",
|
|
1653
|
+
category: "frontend"
|
|
1654
|
+
},
|
|
1655
|
+
// Communication
|
|
1656
|
+
"email": {
|
|
1657
|
+
name: "@flight-framework/email",
|
|
1658
|
+
description: "Email sending",
|
|
1659
|
+
category: "communication"
|
|
1660
|
+
},
|
|
1661
|
+
"realtime": {
|
|
1662
|
+
name: "@flight-framework/realtime",
|
|
1663
|
+
description: "WebSocket and real-time features",
|
|
1664
|
+
category: "communication"
|
|
1665
|
+
},
|
|
1666
|
+
// Deployment
|
|
1667
|
+
"helpers": {
|
|
1668
|
+
name: "@flight-framework/helpers",
|
|
1669
|
+
description: "Optional helper utilities (detection, suggestions)",
|
|
1670
|
+
category: "utilities"
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
async function addCommand(packageName) {
|
|
1674
|
+
const cwd = process.cwd();
|
|
1675
|
+
if (!existsSync6(join5(cwd, "package.json"))) {
|
|
1676
|
+
console.log(pc5.red("No package.json found. Run this command from your project directory."));
|
|
1677
|
+
process.exit(1);
|
|
1678
|
+
}
|
|
1679
|
+
if (!packageName) {
|
|
1680
|
+
showAvailablePackages();
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
const pkg = PACKAGES[packageName];
|
|
1684
|
+
if (!pkg) {
|
|
1685
|
+
console.log(pc5.red(`Unknown package: ${packageName}`));
|
|
1686
|
+
console.log(pc5.dim("Run `flight add` to see available packages."));
|
|
1687
|
+
process.exit(1);
|
|
1688
|
+
}
|
|
1689
|
+
console.log(`
|
|
1690
|
+
${pc5.cyan("Adding")} ${pc5.bold(pkg.name)}...`);
|
|
1691
|
+
console.log(pc5.dim(pkg.description));
|
|
1692
|
+
console.log();
|
|
1693
|
+
const pm = detectPackageManager2();
|
|
1694
|
+
try {
|
|
1695
|
+
const installCmd = pm === "npm" ? "npm install" : `${pm} add`;
|
|
1696
|
+
execSync2(`${installCmd} ${pkg.name}`, { cwd, stdio: "inherit" });
|
|
1697
|
+
console.log(`
|
|
1698
|
+
${pc5.green("[OK]")} ${pkg.name} added successfully!`);
|
|
1699
|
+
showNextSteps(packageName, pkg);
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
console.log(pc5.red(`
|
|
1702
|
+
Failed to install ${pkg.name}`));
|
|
1703
|
+
process.exit(1);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
function detectPackageManager2() {
|
|
1707
|
+
const cwd = process.cwd();
|
|
1708
|
+
if (existsSync6(join5(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
1709
|
+
if (existsSync6(join5(cwd, "yarn.lock"))) return "yarn";
|
|
1710
|
+
if (existsSync6(join5(cwd, "bun.lockb"))) return "bun";
|
|
1711
|
+
return "npm";
|
|
1712
|
+
}
|
|
1713
|
+
function showAvailablePackages() {
|
|
1714
|
+
console.log(`
|
|
1715
|
+
${pc5.cyan("Available Flight packages:")}
|
|
1716
|
+
|
|
1717
|
+
${pc5.bold("Core")}
|
|
1718
|
+
${pc5.green("http")} ${pc5.dim("HTTP server with routing and middleware")}
|
|
1719
|
+
${pc5.green("core")} ${pc5.dim("File-based routing and configuration")}
|
|
1720
|
+
|
|
1721
|
+
${pc5.bold("Data")}
|
|
1722
|
+
${pc5.green("db")} ${pc5.dim("Database abstraction layer")}
|
|
1723
|
+
${pc5.green("cache")} ${pc5.dim("Caching with multiple adapters")}
|
|
1724
|
+
|
|
1725
|
+
${pc5.bold("Auth")}
|
|
1726
|
+
${pc5.green("auth")} ${pc5.dim("Authentication adapters")}
|
|
1727
|
+
|
|
1728
|
+
${pc5.bold("Frontend")}
|
|
1729
|
+
${pc5.green("forms")} ${pc5.dim("Type-safe form handling")}
|
|
1730
|
+
${pc5.green("i18n")} ${pc5.dim("Internationalization")}
|
|
1731
|
+
${pc5.green("seo")} ${pc5.dim("SEO utilities")}
|
|
1732
|
+
${pc5.green("image")} ${pc5.dim("Image optimization")}
|
|
1733
|
+
|
|
1734
|
+
${pc5.bold("Communication")}
|
|
1735
|
+
${pc5.green("email")} ${pc5.dim("Email sending")}
|
|
1736
|
+
${pc5.green("realtime")} ${pc5.dim("WebSocket and real-time features")}
|
|
1737
|
+
|
|
1738
|
+
${pc5.bold("Utilities")}
|
|
1739
|
+
${pc5.green("helpers")} ${pc5.dim("Optional helper utilities")}
|
|
1740
|
+
|
|
1741
|
+
${pc5.cyan("Usage:")}
|
|
1742
|
+
flight add http
|
|
1743
|
+
flight add db
|
|
1744
|
+
flight add auth
|
|
1745
|
+
`);
|
|
1746
|
+
}
|
|
1747
|
+
function showNextSteps(packageName, pkg) {
|
|
1748
|
+
const examples = {
|
|
1749
|
+
"http": `
|
|
1750
|
+
${pc5.cyan("Quick example:")}
|
|
1751
|
+
|
|
1752
|
+
import { createServer } from '@flight-framework/http';
|
|
1753
|
+
|
|
1754
|
+
const app = createServer();
|
|
1755
|
+
app.get('/', (c) => c.json({ hello: 'world' }));
|
|
1756
|
+
|
|
1757
|
+
export default { port: 3000, fetch: app.fetch };
|
|
1758
|
+
`,
|
|
1759
|
+
"db": `
|
|
1760
|
+
${pc5.cyan("Quick example:")}
|
|
1761
|
+
|
|
1762
|
+
import { createDb } from '@flight-framework/db';
|
|
1763
|
+
import { postgres } from '@flight-framework/db/postgres';
|
|
1764
|
+
|
|
1765
|
+
const db = createDb(postgres({ connectionString: process.env.DATABASE_URL }));
|
|
1766
|
+
const users = await db.query('SELECT * FROM users');
|
|
1767
|
+
|
|
1768
|
+
${pc5.dim("You may also need a driver:")}
|
|
1769
|
+
npm install pg ${pc5.dim("# PostgreSQL")}
|
|
1770
|
+
npm install @libsql/client ${pc5.dim("# Turso/SQLite")}
|
|
1771
|
+
npm install @neondatabase/serverless ${pc5.dim("# Neon")}
|
|
1772
|
+
`,
|
|
1773
|
+
"cache": `
|
|
1774
|
+
${pc5.cyan("Quick example:")}
|
|
1775
|
+
|
|
1776
|
+
import { createCache, lru } from '@flight-framework/cache';
|
|
1777
|
+
|
|
1778
|
+
const cache = createCache(lru({ max: 1000 }));
|
|
1779
|
+
await cache.set('key', { data: 'value' });
|
|
1780
|
+
const value = await cache.get('key');
|
|
1781
|
+
`,
|
|
1782
|
+
"auth": `
|
|
1783
|
+
${pc5.cyan("Quick example:")}
|
|
1784
|
+
|
|
1785
|
+
import { createAuth } from '@flight-framework/auth';
|
|
1786
|
+
import { betterAuth } from '@flight-framework/auth/better-auth';
|
|
1787
|
+
|
|
1788
|
+
const auth = createAuth(betterAuth({ db }));
|
|
1789
|
+
const user = await auth.getUser(request);
|
|
1790
|
+
`,
|
|
1791
|
+
"forms": `
|
|
1792
|
+
${pc5.cyan("Quick example:")}
|
|
1793
|
+
|
|
1794
|
+
import { createForm } from '@flight-framework/forms';
|
|
1795
|
+
import { zodAdapter } from '@flight-framework/forms/adapters/zod';
|
|
1796
|
+
|
|
1797
|
+
const form = createForm({ schema: zodAdapter(schema) });
|
|
1798
|
+
const result = form.validate(data);
|
|
1799
|
+
`
|
|
1800
|
+
};
|
|
1801
|
+
if (examples[packageName]) {
|
|
1802
|
+
console.log(examples[packageName]);
|
|
1803
|
+
}
|
|
1804
|
+
console.log(`${pc5.dim("Docs:")} https://flight.dev/docs/packages/${packageName}`);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1297
1807
|
// src/index.ts
|
|
1298
1808
|
var cli = cac("flight");
|
|
1299
1809
|
var LOGO = `
|
|
1300
|
-
${
|
|
1301
|
-
${
|
|
1302
|
-
${
|
|
1303
|
-
${
|
|
1304
|
-
${
|
|
1305
|
-
${
|
|
1810
|
+
${pc6.cyan(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557")}
|
|
1811
|
+
${pc6.cyan(" \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D")}
|
|
1812
|
+
${pc6.cyan(" \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 ")}
|
|
1813
|
+
${pc6.cyan(" \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 ")}
|
|
1814
|
+
${pc6.cyan(" \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ")}
|
|
1815
|
+
${pc6.cyan(" \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D ")}
|
|
1306
1816
|
|
|
1307
|
-
${
|
|
1308
|
-
${
|
|
1817
|
+
${pc6.dim("The Agnostic Full-Stack Framework")}
|
|
1818
|
+
${pc6.dim("Maximum Flexibility. Zero Lock-in.")}
|
|
1309
1819
|
`;
|
|
1310
1820
|
function printLogo() {
|
|
1311
1821
|
console.log(LOGO);
|
|
1312
1822
|
}
|
|
1313
1823
|
cli.version(VERSION);
|
|
1314
1824
|
cli.help();
|
|
1315
|
-
cli.command("create [name]", "Create a new Flight project").option("-t, --template <template>", "Project template to use", { default: "basic" }).option("--ui <framework>", "UI framework (react, vue, svelte, solid, vanilla)").option("--ts", "Use TypeScript", { default: true }).option("--git", "Initialize git repository", { default: true }).option("--install", "Install dependencies", { default: true }).action(createCommand);
|
|
1825
|
+
cli.command("create [name]", "Create a new Flight project").option("-t, --template <template>", "Project template to use", { default: "basic" }).option("--ui <framework>", "UI framework (react, vue, svelte, solid, vanilla)").option("--use-case <useCase>", "Use-case template (blog, ecommerce, saas, api, docs)").option("--ts", "Use TypeScript", { default: true }).option("--git", "Initialize git repository", { default: true }).option("--install", "Install dependencies", { default: true }).option("--raw", "Create raw project (100% Web Standards, zero dependencies)").option("--empty", "Create empty project (just package.json)").option("--minimal", "Create minimal project (single server file with Flight)").action(createCommand);
|
|
1826
|
+
cli.command("add [package]", "Add a Flight package to your project").action(addCommand);
|
|
1316
1827
|
cli.command("dev", "Start development server").option("-p, --port <port>", "Port to listen on").option("-h, --host <host>", "Host to bind to").option("--open", "Open browser on start").option("--https", "Enable HTTPS").option("--ssr", "Enable Server-Side Rendering").action(devCommand);
|
|
1317
1828
|
cli.command("build", "Build for production").option("--outDir <dir>", "Output directory").option("--sourcemap", "Generate source maps").option("--minify", "Minify output", { default: true }).action(buildCommand);
|
|
1318
1829
|
cli.command("preview", "Preview production build").option("-p, --port <port>", "Port to listen on").option("-h, --host <host>", "Host to bind to").option("--open", "Open browser on start").action(previewCommand);
|
|
@@ -1326,7 +1837,7 @@ function run() {
|
|
|
1326
1837
|
}
|
|
1327
1838
|
cli.runMatchedCommand();
|
|
1328
1839
|
} catch (error) {
|
|
1329
|
-
console.error(
|
|
1840
|
+
console.error(pc6.red("Error:"), error instanceof Error ? error.message : error);
|
|
1330
1841
|
process.exit(1);
|
|
1331
1842
|
}
|
|
1332
1843
|
}
|