@kozojs/cli 0.1.21 → 0.1.22
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/lib/{index.js → index.mjs} +194 -215
- package/package.json +51 -51
- package/lib/index.d.ts +0 -2
|
@@ -1,43 +1,18 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
-
mod
|
|
24
|
-
));
|
|
25
|
-
|
|
26
1
|
// src/index.ts
|
|
27
|
-
|
|
2
|
+
import { Command } from "commander";
|
|
28
3
|
|
|
29
4
|
// src/commands/new.ts
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import pc2 from "picocolors";
|
|
7
|
+
import { execa } from "execa";
|
|
33
8
|
|
|
34
9
|
// src/utils/scaffold.ts
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
import fs from "fs-extra";
|
|
11
|
+
import path from "path";
|
|
37
12
|
async function scaffoldProject(options) {
|
|
38
13
|
const { projectName, runtime, database, dbPort, auth, packageSource, template, frontend, extras } = options;
|
|
39
|
-
const projectDir =
|
|
40
|
-
const kozoCoreDep = packageSource === "local" ? "workspace:*" : "^0.
|
|
14
|
+
const projectDir = path.resolve(process.cwd(), projectName);
|
|
15
|
+
const kozoCoreDep = packageSource === "local" ? "workspace:*" : "^0.3.1";
|
|
41
16
|
if (frontend !== "none") {
|
|
42
17
|
await scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, runtime, database, dbPort, auth, frontend, extras, template);
|
|
43
18
|
return;
|
|
@@ -55,9 +30,9 @@ async function scaffoldProject(options) {
|
|
|
55
30
|
if (extras.includes("github-actions")) await createGitHubActions(projectDir);
|
|
56
31
|
return;
|
|
57
32
|
}
|
|
58
|
-
await
|
|
59
|
-
await
|
|
60
|
-
await
|
|
33
|
+
await fs.ensureDir(path.join(projectDir, "src", "routes"));
|
|
34
|
+
await fs.ensureDir(path.join(projectDir, "src", "db"));
|
|
35
|
+
await fs.ensureDir(path.join(projectDir, "src", "services"));
|
|
61
36
|
const packageJson = {
|
|
62
37
|
name: projectName,
|
|
63
38
|
version: "0.1.0",
|
|
@@ -72,6 +47,7 @@ async function scaffoldProject(options) {
|
|
|
72
47
|
},
|
|
73
48
|
dependencies: {
|
|
74
49
|
"@kozojs/core": kozoCoreDep,
|
|
50
|
+
"uWebSockets.js": "github:uNetworking/uWebSockets.js#6609a88ffa9a16ac5158046761356ce03250a0df",
|
|
75
51
|
hono: "^4.6.0",
|
|
76
52
|
zod: "^3.23.0",
|
|
77
53
|
"drizzle-orm": "^0.36.0",
|
|
@@ -87,7 +63,7 @@ async function scaffoldProject(options) {
|
|
|
87
63
|
...database === "sqlite" && { "@types/better-sqlite3": "^7.6.0" }
|
|
88
64
|
}
|
|
89
65
|
};
|
|
90
|
-
await
|
|
66
|
+
await fs.writeJSON(path.join(projectDir, "package.json"), packageJson, { spaces: 2 });
|
|
91
67
|
const tsconfig = {
|
|
92
68
|
compilerOptions: {
|
|
93
69
|
target: "ES2022",
|
|
@@ -103,7 +79,7 @@ async function scaffoldProject(options) {
|
|
|
103
79
|
include: ["src/**/*"],
|
|
104
80
|
exclude: ["node_modules", "dist"]
|
|
105
81
|
};
|
|
106
|
-
await
|
|
82
|
+
await fs.writeJSON(path.join(projectDir, "tsconfig.json"), tsconfig, { spaces: 2 });
|
|
107
83
|
const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
|
|
108
84
|
|
|
109
85
|
export default defineConfig({
|
|
@@ -115,21 +91,21 @@ export default defineConfig({
|
|
|
115
91
|
}
|
|
116
92
|
});
|
|
117
93
|
`;
|
|
118
|
-
await
|
|
94
|
+
await fs.writeFile(path.join(projectDir, "drizzle.config.ts"), drizzleConfig);
|
|
119
95
|
const envExample = `# Database
|
|
120
96
|
${database === "sqlite" ? "# SQLite uses local file, no URL needed" : "DATABASE_URL="}
|
|
121
97
|
|
|
122
98
|
# Server
|
|
123
99
|
PORT=3000
|
|
124
100
|
`;
|
|
125
|
-
await
|
|
101
|
+
await fs.writeFile(path.join(projectDir, ".env.example"), envExample);
|
|
126
102
|
const gitignore = `node_modules/
|
|
127
103
|
dist/
|
|
128
104
|
.env
|
|
129
105
|
*.db
|
|
130
106
|
.turbo/
|
|
131
107
|
`;
|
|
132
|
-
await
|
|
108
|
+
await fs.writeFile(path.join(projectDir, ".gitignore"), gitignore);
|
|
133
109
|
const indexTs = `import { createKozo } from '@kozojs/core';
|
|
134
110
|
import { services } from './services/index.js';
|
|
135
111
|
|
|
@@ -151,9 +127,9 @@ const app = createKozo({
|
|
|
151
127
|
}
|
|
152
128
|
});
|
|
153
129
|
|
|
154
|
-
app.
|
|
130
|
+
await app.nativeListen();
|
|
155
131
|
`;
|
|
156
|
-
await
|
|
132
|
+
await fs.writeFile(path.join(projectDir, "src", "index.ts"), indexTs);
|
|
157
133
|
const servicesTs = `import { db } from '../db/index.js';
|
|
158
134
|
|
|
159
135
|
export const services = {
|
|
@@ -167,14 +143,14 @@ declare module '@kozojs/core' {
|
|
|
167
143
|
}
|
|
168
144
|
}
|
|
169
145
|
`;
|
|
170
|
-
await
|
|
146
|
+
await fs.writeFile(path.join(projectDir, "src", "services", "index.ts"), servicesTs);
|
|
171
147
|
const schemaTs = getDatabaseSchema(database);
|
|
172
|
-
await
|
|
148
|
+
await fs.writeFile(path.join(projectDir, "src", "db", "schema.ts"), schemaTs);
|
|
173
149
|
const dbIndexTs = getDatabaseIndex(database);
|
|
174
|
-
await
|
|
150
|
+
await fs.writeFile(path.join(projectDir, "src", "db", "index.ts"), dbIndexTs);
|
|
175
151
|
if (database === "sqlite") {
|
|
176
152
|
const seedTs = getSQLiteSeed();
|
|
177
|
-
await
|
|
153
|
+
await fs.writeFile(path.join(projectDir, "src", "db", "seed.ts"), seedTs);
|
|
178
154
|
}
|
|
179
155
|
await createExampleRoutes(projectDir);
|
|
180
156
|
const readme = `# ${projectName}
|
|
@@ -250,7 +226,7 @@ ${database === "sqlite" ? "## SQLite Notes\n\nThe database is automatically init
|
|
|
250
226
|
- [Drizzle ORM](https://orm.drizzle.team)
|
|
251
227
|
- [Hono](https://hono.dev)
|
|
252
228
|
`;
|
|
253
|
-
await
|
|
229
|
+
await fs.writeFile(path.join(projectDir, "README.md"), readme);
|
|
254
230
|
if (database !== "none" && database !== "sqlite") await createDockerCompose(projectDir, projectName, database, dbPort);
|
|
255
231
|
if (extras.includes("docker")) await createDockerfile(projectDir, runtime);
|
|
256
232
|
if (extras.includes("github-actions")) await createGitHubActions(projectDir);
|
|
@@ -370,8 +346,8 @@ async function createExampleRoutes(projectDir) {
|
|
|
370
346
|
};
|
|
371
347
|
};
|
|
372
348
|
`;
|
|
373
|
-
await
|
|
374
|
-
await
|
|
349
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "index.ts"), indexRoute);
|
|
350
|
+
await fs.ensureDir(path.join(projectDir, "src", "routes", "users"));
|
|
375
351
|
const getUsersRoute = `import type { HandlerContext } from '@kozojs/core';
|
|
376
352
|
import { users } from '../../db/schema.js';
|
|
377
353
|
|
|
@@ -385,7 +361,7 @@ export default async ({ services: { db } }: HandlerContext) => {
|
|
|
385
361
|
return { users: allUsers };
|
|
386
362
|
};
|
|
387
363
|
`;
|
|
388
|
-
await
|
|
364
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "users", "get.ts"), getUsersRoute);
|
|
389
365
|
const postUsersRoute = `import { z } from 'zod';
|
|
390
366
|
import type { HandlerContext } from '@kozojs/core';
|
|
391
367
|
import { users } from '../../db/schema.js';
|
|
@@ -413,26 +389,26 @@ export default async ({ body, services: { db } }: HandlerContext<Body>) => {
|
|
|
413
389
|
return { success: true, user };
|
|
414
390
|
};
|
|
415
391
|
`;
|
|
416
|
-
await
|
|
392
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "users", "post.ts"), postUsersRoute);
|
|
417
393
|
}
|
|
418
394
|
async function scaffoldCompleteTemplate(projectDir, projectName, kozoCoreDep, runtime, database = "none", dbPort, auth = true) {
|
|
419
|
-
await
|
|
420
|
-
await
|
|
421
|
-
await
|
|
422
|
-
await
|
|
423
|
-
await
|
|
424
|
-
await
|
|
425
|
-
await
|
|
426
|
-
await
|
|
427
|
-
await
|
|
428
|
-
await
|
|
395
|
+
await fs.ensureDir(projectDir);
|
|
396
|
+
await fs.ensureDir(path.join(projectDir, "src"));
|
|
397
|
+
await fs.ensureDir(path.join(projectDir, "src", "schemas"));
|
|
398
|
+
await fs.ensureDir(path.join(projectDir, "src", "routes"));
|
|
399
|
+
await fs.ensureDir(path.join(projectDir, "src", "routes", "auth"));
|
|
400
|
+
await fs.ensureDir(path.join(projectDir, "src", "routes", "users"));
|
|
401
|
+
await fs.ensureDir(path.join(projectDir, "src", "routes", "posts"));
|
|
402
|
+
await fs.ensureDir(path.join(projectDir, "src", "middleware"));
|
|
403
|
+
await fs.ensureDir(path.join(projectDir, "src", "utils"));
|
|
404
|
+
await fs.ensureDir(path.join(projectDir, "src", "data"));
|
|
429
405
|
const hasDb = database !== "none";
|
|
430
406
|
if (hasDb) {
|
|
431
|
-
await
|
|
432
|
-
await
|
|
433
|
-
await
|
|
407
|
+
await fs.ensureDir(path.join(projectDir, "src", "db"));
|
|
408
|
+
await fs.writeFile(path.join(projectDir, "src", "db", "schema.ts"), getDatabaseSchema(database));
|
|
409
|
+
await fs.writeFile(path.join(projectDir, "src", "db", "index.ts"), getDatabaseIndex(database));
|
|
434
410
|
if (database === "sqlite") {
|
|
435
|
-
await
|
|
411
|
+
await fs.writeFile(path.join(projectDir, "src", "db", "seed.ts"), getSQLiteSeed());
|
|
436
412
|
}
|
|
437
413
|
}
|
|
438
414
|
const packageJson = {
|
|
@@ -454,6 +430,7 @@ async function scaffoldCompleteTemplate(projectDir, projectName, kozoCoreDep, ru
|
|
|
454
430
|
"@kozojs/core": kozoCoreDep,
|
|
455
431
|
...auth && { "@kozojs/auth": kozoCoreDep === "workspace:*" ? "workspace:*" : "^0.1.0" },
|
|
456
432
|
"@hono/node-server": "^1.13.0",
|
|
433
|
+
...runtime === "node" && { "uWebSockets.js": "github:uNetworking/uWebSockets.js#6609a88ffa9a16ac5158046761356ce03250a0df" },
|
|
457
434
|
hono: "^4.6.0",
|
|
458
435
|
zod: "^3.23.0",
|
|
459
436
|
dotenv: "^16.4.0",
|
|
@@ -470,7 +447,7 @@ async function scaffoldCompleteTemplate(projectDir, projectName, kozoCoreDep, ru
|
|
|
470
447
|
...database === "sqlite" && { "@types/better-sqlite3": "^7.6.0" }
|
|
471
448
|
}
|
|
472
449
|
};
|
|
473
|
-
await
|
|
450
|
+
await fs.writeJSON(path.join(projectDir, "package.json"), packageJson, { spaces: 2 });
|
|
474
451
|
const tsconfig = {
|
|
475
452
|
compilerOptions: {
|
|
476
453
|
target: "ES2022",
|
|
@@ -488,14 +465,14 @@ async function scaffoldCompleteTemplate(projectDir, projectName, kozoCoreDep, ru
|
|
|
488
465
|
include: ["src/**/*"],
|
|
489
466
|
exclude: ["node_modules", "dist"]
|
|
490
467
|
};
|
|
491
|
-
await
|
|
468
|
+
await fs.writeJSON(path.join(projectDir, "tsconfig.json"), tsconfig, { spaces: 2 });
|
|
492
469
|
const gitignore = `node_modules/
|
|
493
470
|
dist/
|
|
494
471
|
.env
|
|
495
472
|
.turbo/
|
|
496
473
|
*.log
|
|
497
474
|
`;
|
|
498
|
-
await
|
|
475
|
+
await fs.writeFile(path.join(projectDir, ".gitignore"), gitignore);
|
|
499
476
|
const pgPort = dbPort ?? 5436;
|
|
500
477
|
const dbUrl = database === "postgresql" ? `postgresql://postgres:postgres@localhost:${pgPort}/${projectName}` : database === "mysql" ? `mysql://root:root@localhost:3306/${projectName}` : void 0;
|
|
501
478
|
const envExample = `# Server
|
|
@@ -515,8 +492,8 @@ CORS_ORIGIN=http://localhost:5173
|
|
|
515
492
|
RATE_LIMIT_MAX=100
|
|
516
493
|
RATE_LIMIT_WINDOW=60000
|
|
517
494
|
`;
|
|
518
|
-
await
|
|
519
|
-
await
|
|
495
|
+
await fs.writeFile(path.join(projectDir, ".env.example"), envExample);
|
|
496
|
+
await fs.writeFile(path.join(projectDir, ".env"), envExample);
|
|
520
497
|
if (hasDb) {
|
|
521
498
|
const dialect = database === "postgresql" ? "postgresql" : database === "mysql" ? "mysql" : "sqlite";
|
|
522
499
|
const drizzleConfig = `import { defineConfig } from 'drizzle-kit';
|
|
@@ -531,7 +508,7 @@ export default defineConfig({
|
|
|
531
508
|
},
|
|
532
509
|
});
|
|
533
510
|
`;
|
|
534
|
-
await
|
|
511
|
+
await fs.writeFile(path.join(projectDir, "drizzle.config.ts"), drizzleConfig);
|
|
535
512
|
}
|
|
536
513
|
const indexTs = `import { createKozo, cors, logger, rateLimit } from '@kozojs/core';
|
|
537
514
|
${auth ? "import { authenticateJWT } from '@kozojs/auth';" : ""}
|
|
@@ -576,7 +553,7 @@ console.log('');
|
|
|
576
553
|
console.log('\u{1F525} Kozo server starting\u2026');
|
|
577
554
|
console.log('');
|
|
578
555
|
|
|
579
|
-
await app.
|
|
556
|
+
await app.nativeListen(PORT);
|
|
580
557
|
console.log('');
|
|
581
558
|
console.log('\u{1F4DA} Endpoints:');
|
|
582
559
|
console.log(' GET /health Health check');
|
|
@@ -594,7 +571,7 @@ console.log('');
|
|
|
594
571
|
console.log('\u{1F512} Middleware: CORS \xB7 Rate limit \xB7 JWT \xB7 Logger');
|
|
595
572
|
console.log('\u{1F6E1}\uFE0F Graceful shutdown enabled (SIGTERM / SIGINT)');
|
|
596
573
|
`;
|
|
597
|
-
await
|
|
574
|
+
await fs.writeFile(path.join(projectDir, "src", "index.ts"), indexTs);
|
|
598
575
|
await createCompleteSchemas(projectDir);
|
|
599
576
|
await createCompleteUtils(projectDir);
|
|
600
577
|
await createCompleteDataStore(projectDir);
|
|
@@ -759,7 +736,7 @@ Request \u2500\u2500\u25BA \u2502 uWebSockets \u2502 C++ HTTP parser + epoll/kq
|
|
|
759
736
|
|
|
760
737
|
Built with \u2764\uFE0F using Kozo Framework
|
|
761
738
|
`;
|
|
762
|
-
await
|
|
739
|
+
await fs.writeFile(path.join(projectDir, "README.md"), readme);
|
|
763
740
|
}
|
|
764
741
|
async function createCompleteSchemas(projectDir) {
|
|
765
742
|
const userSchemas = `import { z } from 'zod';
|
|
@@ -788,7 +765,7 @@ export type User = z.infer<typeof UserSchema>;
|
|
|
788
765
|
export type CreateUser = z.infer<typeof CreateUserSchema>;
|
|
789
766
|
export type UpdateUser = z.infer<typeof UpdateUserSchema>;
|
|
790
767
|
`;
|
|
791
|
-
await
|
|
768
|
+
await fs.writeFile(path.join(projectDir, "src", "schemas", "user.ts"), userSchemas);
|
|
792
769
|
const postSchemas = `import { z } from 'zod';
|
|
793
770
|
import { UserSchema } from './user.js';
|
|
794
771
|
|
|
@@ -818,7 +795,7 @@ export type Post = z.infer<typeof PostSchema>;
|
|
|
818
795
|
export type PostWithAuthor = z.infer<typeof PostWithAuthorSchema>;
|
|
819
796
|
export type CreatePost = z.infer<typeof CreatePostSchema>;
|
|
820
797
|
`;
|
|
821
|
-
await
|
|
798
|
+
await fs.writeFile(path.join(projectDir, "src", "schemas", "post.ts"), postSchemas);
|
|
822
799
|
const commonSchemas = `import { z } from 'zod';
|
|
823
800
|
|
|
824
801
|
export const PaginationSchema = z.object({
|
|
@@ -835,7 +812,7 @@ export const PostFiltersSchema = z.object({
|
|
|
835
812
|
export type Pagination = z.infer<typeof PaginationSchema>;
|
|
836
813
|
export type PostFilters = z.infer<typeof PostFiltersSchema>;
|
|
837
814
|
`;
|
|
838
|
-
await
|
|
815
|
+
await fs.writeFile(path.join(projectDir, "src", "schemas", "common.ts"), commonSchemas);
|
|
839
816
|
}
|
|
840
817
|
async function createCompleteUtils(projectDir) {
|
|
841
818
|
const helpers = `export function generateUUID(): string {
|
|
@@ -858,7 +835,7 @@ export function paginate<T>(items: T[], page: number, limit: number) {
|
|
|
858
835
|
};
|
|
859
836
|
}
|
|
860
837
|
`;
|
|
861
|
-
await
|
|
838
|
+
await fs.writeFile(path.join(projectDir, "src", "utils", "helpers.ts"), helpers);
|
|
862
839
|
}
|
|
863
840
|
async function createCompleteDataStore(projectDir) {
|
|
864
841
|
const store = `import type { User } from '../schemas/user.js';
|
|
@@ -896,7 +873,7 @@ export const posts: Post[] = [
|
|
|
896
873
|
},
|
|
897
874
|
];
|
|
898
875
|
`;
|
|
899
|
-
await
|
|
876
|
+
await fs.writeFile(path.join(projectDir, "src", "data", "store.ts"), store);
|
|
900
877
|
}
|
|
901
878
|
async function createCompleteRoutes(projectDir) {
|
|
902
879
|
const healthRoute = `import type { Kozo } from '@kozojs/core';
|
|
@@ -912,7 +889,7 @@ export function registerHealthRoute(app: Kozo) {
|
|
|
912
889
|
});
|
|
913
890
|
}
|
|
914
891
|
`;
|
|
915
|
-
await
|
|
892
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "health.ts"), healthRoute);
|
|
916
893
|
const statsRoute = `import { z } from 'zod';
|
|
917
894
|
import type { Kozo } from '@kozojs/core';
|
|
918
895
|
import { users } from '../data/store.js';
|
|
@@ -967,7 +944,7 @@ export function registerStatsRoute(app: Kozo) {
|
|
|
967
944
|
});
|
|
968
945
|
}
|
|
969
946
|
`;
|
|
970
|
-
await
|
|
947
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "stats.ts"), statsRoute);
|
|
971
948
|
const authRoutes = `import { z } from 'zod';
|
|
972
949
|
import type { Kozo } from '@kozojs/core';
|
|
973
950
|
import { createJWT } from '@kozojs/auth';
|
|
@@ -1013,7 +990,7 @@ export function registerAuthRoutes(app: Kozo) {
|
|
|
1013
990
|
});
|
|
1014
991
|
}
|
|
1015
992
|
`;
|
|
1016
|
-
await
|
|
993
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "auth", "index.ts"), authRoutes);
|
|
1017
994
|
const userRoutes = `import { z } from 'zod';
|
|
1018
995
|
import type { Kozo } from '@kozojs/core';
|
|
1019
996
|
import { UserSchema, CreateUserSchema, UpdateUserSchema } from '../../schemas/user.js';
|
|
@@ -1098,7 +1075,7 @@ export function registerUserRoutes(app: Kozo) {
|
|
|
1098
1075
|
});
|
|
1099
1076
|
}
|
|
1100
1077
|
`;
|
|
1101
|
-
await
|
|
1078
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "users", "index.ts"), userRoutes);
|
|
1102
1079
|
const postRoutes = `import { z } from 'zod';
|
|
1103
1080
|
import type { Kozo } from '@kozojs/core';
|
|
1104
1081
|
import { PostSchema, PostWithAuthorSchema, CreatePostSchema } from '../../schemas/post.js';
|
|
@@ -1174,10 +1151,10 @@ export function registerPostRoutes(app: Kozo) {
|
|
|
1174
1151
|
});
|
|
1175
1152
|
}
|
|
1176
1153
|
`;
|
|
1177
|
-
await
|
|
1154
|
+
await fs.writeFile(path.join(projectDir, "src", "routes", "posts", "index.ts"), postRoutes);
|
|
1178
1155
|
}
|
|
1179
1156
|
async function scaffoldApiOnlyTemplate(projectDir, projectName, kozoCoreDep, runtime) {
|
|
1180
|
-
await
|
|
1157
|
+
await fs.ensureDir(path.join(projectDir, "src"));
|
|
1181
1158
|
const packageJson = {
|
|
1182
1159
|
name: projectName,
|
|
1183
1160
|
version: "1.0.0",
|
|
@@ -1191,7 +1168,8 @@ async function scaffoldApiOnlyTemplate(projectDir, projectName, kozoCoreDep, run
|
|
|
1191
1168
|
"@kozojs/core": kozoCoreDep,
|
|
1192
1169
|
hono: "^4.6.0",
|
|
1193
1170
|
zod: "^3.23.0",
|
|
1194
|
-
...runtime === "node" && { "@hono/node-server": "^1.13.0" }
|
|
1171
|
+
...runtime === "node" && { "@hono/node-server": "^1.13.0" },
|
|
1172
|
+
...runtime === "node" && { "uWebSockets.js": "github:uNetworking/uWebSockets.js#6609a88ffa9a16ac5158046761356ce03250a0df" }
|
|
1195
1173
|
},
|
|
1196
1174
|
devDependencies: {
|
|
1197
1175
|
"@types/node": "^22.0.0",
|
|
@@ -1199,7 +1177,7 @@ async function scaffoldApiOnlyTemplate(projectDir, projectName, kozoCoreDep, run
|
|
|
1199
1177
|
typescript: "^5.6.0"
|
|
1200
1178
|
}
|
|
1201
1179
|
};
|
|
1202
|
-
await
|
|
1180
|
+
await fs.writeJSON(path.join(projectDir, "package.json"), packageJson, { spaces: 2 });
|
|
1203
1181
|
const tsconfig = {
|
|
1204
1182
|
compilerOptions: {
|
|
1205
1183
|
target: "ES2022",
|
|
@@ -1214,7 +1192,7 @@ async function scaffoldApiOnlyTemplate(projectDir, projectName, kozoCoreDep, run
|
|
|
1214
1192
|
include: ["src/**/*"],
|
|
1215
1193
|
exclude: ["node_modules", "dist"]
|
|
1216
1194
|
};
|
|
1217
|
-
await
|
|
1195
|
+
await fs.writeJSON(path.join(projectDir, "tsconfig.json"), tsconfig, { spaces: 2 });
|
|
1218
1196
|
const indexTs = `import { createKozo } from '@kozojs/core';
|
|
1219
1197
|
import { z } from 'zod';
|
|
1220
1198
|
|
|
@@ -1237,8 +1215,8 @@ app.get('/hello/:name', {
|
|
|
1237
1215
|
console.log('\u{1F525} Kozo running on http://localhost:3000');
|
|
1238
1216
|
await app.nativeListen();
|
|
1239
1217
|
`;
|
|
1240
|
-
await
|
|
1241
|
-
await
|
|
1218
|
+
await fs.writeFile(path.join(projectDir, "src", "index.ts"), indexTs);
|
|
1219
|
+
await fs.writeFile(path.join(projectDir, ".gitignore"), "node_modules/\ndist/\n.env\n");
|
|
1242
1220
|
}
|
|
1243
1221
|
async function createDockerCompose(dir, projectName, database, dbPort, includeApiService = false, runtime = "node") {
|
|
1244
1222
|
if (database === "none" || database === "sqlite") return;
|
|
@@ -1319,7 +1297,7 @@ async function createDockerCompose(dir, projectName, database, dbPort, includeAp
|
|
|
1319
1297
|
const volumes = database === "postgresql" ? "\nvolumes:\n postgres_data:\n" : database === "mysql" ? "\nvolumes:\n mysql_data:\n" : "";
|
|
1320
1298
|
const compose = `services:
|
|
1321
1299
|
${services}${volumes}`;
|
|
1322
|
-
await
|
|
1300
|
+
await fs.writeFile(path.join(dir, "docker-compose.yml"), compose);
|
|
1323
1301
|
}
|
|
1324
1302
|
async function createDockerfile(projectDir, runtime) {
|
|
1325
1303
|
const dockerfile = runtime === "bun" ? `FROM oven/bun:1 AS builder
|
|
@@ -1350,11 +1328,11 @@ RUN npm ci --omit=dev
|
|
|
1350
1328
|
EXPOSE 3000
|
|
1351
1329
|
CMD ["node", "dist/index.js"]
|
|
1352
1330
|
`;
|
|
1353
|
-
await
|
|
1354
|
-
await
|
|
1331
|
+
await fs.writeFile(path.join(projectDir, "Dockerfile"), dockerfile);
|
|
1332
|
+
await fs.writeFile(path.join(projectDir, ".dockerignore"), "node_modules\ndist\n.git\n.env\n");
|
|
1355
1333
|
}
|
|
1356
1334
|
async function createGitHubActions(projectDir) {
|
|
1357
|
-
await
|
|
1335
|
+
await fs.ensureDir(path.join(projectDir, ".github", "workflows"));
|
|
1358
1336
|
const workflow = `name: CI
|
|
1359
1337
|
|
|
1360
1338
|
on:
|
|
@@ -1376,15 +1354,15 @@ jobs:
|
|
|
1376
1354
|
- run: npm run build
|
|
1377
1355
|
- run: npm test --if-present
|
|
1378
1356
|
`;
|
|
1379
|
-
await
|
|
1357
|
+
await fs.writeFile(path.join(projectDir, ".github", "workflows", "ci.yml"), workflow);
|
|
1380
1358
|
}
|
|
1381
1359
|
async function scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, runtime, database, dbPort, auth, frontend, extras, template) {
|
|
1382
1360
|
const hasDb = database !== "none";
|
|
1383
|
-
await
|
|
1384
|
-
await
|
|
1385
|
-
if (hasDb) await
|
|
1386
|
-
await
|
|
1387
|
-
await
|
|
1361
|
+
await fs.ensureDir(path.join(projectDir, "apps", "api", "src", "routes"));
|
|
1362
|
+
await fs.ensureDir(path.join(projectDir, "apps", "api", "src", "data"));
|
|
1363
|
+
if (hasDb) await fs.ensureDir(path.join(projectDir, "apps", "api", "src", "db"));
|
|
1364
|
+
await fs.ensureDir(path.join(projectDir, "apps", "web", "src", "lib"));
|
|
1365
|
+
await fs.ensureDir(path.join(projectDir, ".vscode"));
|
|
1388
1366
|
const rootPackageJson = {
|
|
1389
1367
|
name: projectName,
|
|
1390
1368
|
private: true,
|
|
@@ -1393,32 +1371,32 @@ async function scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, ru
|
|
|
1393
1371
|
build: "pnpm run --recursive build"
|
|
1394
1372
|
}
|
|
1395
1373
|
};
|
|
1396
|
-
await
|
|
1397
|
-
await
|
|
1374
|
+
await fs.writeJSON(path.join(projectDir, "package.json"), rootPackageJson, { spaces: 2 });
|
|
1375
|
+
await fs.writeFile(path.join(projectDir, "pnpm-workspace.yaml"), `packages:
|
|
1398
1376
|
- 'apps/*'
|
|
1399
1377
|
`);
|
|
1400
|
-
await
|
|
1378
|
+
await fs.writeFile(path.join(projectDir, ".gitignore"), "node_modules/\ndist/\n.env\n*.log\n");
|
|
1401
1379
|
await scaffoldFullstackApi(projectDir, projectName, kozoCoreDep, runtime, database, dbPort, auth);
|
|
1402
1380
|
await scaffoldFullstackWeb(projectDir, projectName, frontend, auth);
|
|
1403
1381
|
await scaffoldFullstackReadme(projectDir, projectName);
|
|
1404
1382
|
if (database !== "none" && database !== "sqlite") await createDockerCompose(projectDir, projectName, database, dbPort);
|
|
1405
|
-
if (extras.includes("docker")) await createDockerfile(
|
|
1383
|
+
if (extras.includes("docker")) await createDockerfile(path.join(projectDir, "apps", "api"), runtime);
|
|
1406
1384
|
if (extras.includes("github-actions")) await createGitHubActions(projectDir);
|
|
1407
1385
|
}
|
|
1408
1386
|
async function scaffoldFullstackApi(projectDir, projectName, kozoCoreDep, runtime, database = "none", dbPort, auth = true) {
|
|
1409
|
-
const apiDir =
|
|
1387
|
+
const apiDir = path.join(projectDir, "apps", "api");
|
|
1410
1388
|
const hasDb = database !== "none";
|
|
1411
1389
|
if (hasDb) {
|
|
1412
|
-
await
|
|
1413
|
-
await
|
|
1414
|
-
await
|
|
1390
|
+
await fs.ensureDir(path.join(apiDir, "src", "db"));
|
|
1391
|
+
await fs.writeFile(path.join(apiDir, "src", "db", "schema.ts"), getDatabaseSchema(database));
|
|
1392
|
+
await fs.writeFile(path.join(apiDir, "src", "db", "index.ts"), getDatabaseIndex(database));
|
|
1415
1393
|
if (database === "sqlite") {
|
|
1416
|
-
await
|
|
1394
|
+
await fs.writeFile(path.join(apiDir, "src", "db", "seed.ts"), getSQLiteSeed());
|
|
1417
1395
|
}
|
|
1418
1396
|
const dialect = database === "postgresql" ? "postgresql" : database === "mysql" ? "mysql" : "sqlite";
|
|
1419
1397
|
const pgPort = dbPort ?? 5436;
|
|
1420
1398
|
const dbUrl = database === "postgresql" ? `postgresql://postgres:postgres@localhost:${pgPort}/${projectName}` : database === "mysql" ? `mysql://root:root@localhost:3306/${projectName}` : void 0;
|
|
1421
|
-
await
|
|
1399
|
+
await fs.writeFile(path.join(apiDir, "drizzle.config.ts"), `import { defineConfig } from 'drizzle-kit';
|
|
1422
1400
|
import 'dotenv/config';
|
|
1423
1401
|
|
|
1424
1402
|
export default defineConfig({
|
|
@@ -1434,14 +1412,14 @@ export default defineConfig({
|
|
|
1434
1412
|
NODE_ENV=development
|
|
1435
1413
|
${dbUrl ? `DATABASE_URL=${dbUrl}
|
|
1436
1414
|
` : ""}${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
|
|
1437
|
-
await
|
|
1438
|
-
await
|
|
1415
|
+
await fs.writeFile(path.join(apiDir, ".env"), envContent);
|
|
1416
|
+
await fs.writeFile(path.join(apiDir, ".env.example"), envContent);
|
|
1439
1417
|
} else {
|
|
1440
1418
|
const envContent = `PORT=3000
|
|
1441
1419
|
NODE_ENV=development
|
|
1442
1420
|
${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
|
|
1443
|
-
await
|
|
1444
|
-
await
|
|
1421
|
+
await fs.writeFile(path.join(apiDir, ".env"), envContent);
|
|
1422
|
+
await fs.writeFile(path.join(apiDir, ".env.example"), envContent);
|
|
1445
1423
|
}
|
|
1446
1424
|
const apiPackageJson = {
|
|
1447
1425
|
name: `@${projectName}/api`,
|
|
@@ -1463,6 +1441,7 @@ ${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
|
|
|
1463
1441
|
zod: "^3.23.0",
|
|
1464
1442
|
dotenv: "^16.4.0",
|
|
1465
1443
|
...runtime === "node" && { "@hono/node-server": "^1.13.0" },
|
|
1444
|
+
...runtime === "node" && { "uWebSockets.js": "github:uNetworking/uWebSockets.js#6609a88ffa9a16ac5158046761356ce03250a0df" },
|
|
1466
1445
|
...hasDb && { "drizzle-orm": "^0.36.0" },
|
|
1467
1446
|
...database === "postgresql" && { postgres: "^3.4.0" },
|
|
1468
1447
|
...database === "mysql" && { mysql2: "^3.11.0" },
|
|
@@ -1476,7 +1455,7 @@ ${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
|
|
|
1476
1455
|
...database === "sqlite" && { "@types/better-sqlite3": "^7.6.0" }
|
|
1477
1456
|
}
|
|
1478
1457
|
};
|
|
1479
|
-
await
|
|
1458
|
+
await fs.writeJSON(path.join(apiDir, "package.json"), apiPackageJson, { spaces: 2 });
|
|
1480
1459
|
const tsconfig = {
|
|
1481
1460
|
compilerOptions: {
|
|
1482
1461
|
target: "ES2022",
|
|
@@ -1491,7 +1470,7 @@ ${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
|
|
|
1491
1470
|
include: ["src/**/*"],
|
|
1492
1471
|
exclude: ["node_modules", "dist"]
|
|
1493
1472
|
};
|
|
1494
|
-
await
|
|
1473
|
+
await fs.writeJSON(path.join(apiDir, "tsconfig.json"), tsconfig, { spaces: 2 });
|
|
1495
1474
|
const authImport = auth ? `import { authenticateJWT } from '@kozojs/auth';
|
|
1496
1475
|
` : "";
|
|
1497
1476
|
const authMiddleware = auth ? `
|
|
@@ -1504,7 +1483,7 @@ app.getApp().use('/api/*', (c, next) => {
|
|
|
1504
1483
|
return _jwt(c, next);
|
|
1505
1484
|
});
|
|
1506
1485
|
` : "";
|
|
1507
|
-
await
|
|
1486
|
+
await fs.outputFile(path.join(apiDir, "src", "index.ts"), `import 'dotenv/config';
|
|
1508
1487
|
import { createKozo } from '@kozojs/core';
|
|
1509
1488
|
${authImport}import { fileURLToPath } from 'node:url';
|
|
1510
1489
|
import { dirname, join } from 'node:path';
|
|
@@ -1517,9 +1496,9 @@ ${authMiddleware}await app.loadRoutes(join(__dirname, 'routes'));
|
|
|
1517
1496
|
export type AppType = typeof app;
|
|
1518
1497
|
|
|
1519
1498
|
console.log(\`\u{1F525} ${projectName} API on http://localhost:\${PORT}\`);
|
|
1520
|
-
await app.listen();
|
|
1499
|
+
${runtime === "node" ? "await app.nativeListen();" : "await app.listen();"}
|
|
1521
1500
|
`);
|
|
1522
|
-
await
|
|
1501
|
+
await fs.outputFile(path.join(apiDir, "src", "schemas", "index.ts"), `import { z } from 'zod';
|
|
1523
1502
|
|
|
1524
1503
|
export const UserSchema = z.object({
|
|
1525
1504
|
id: z.string(),
|
|
@@ -1582,7 +1561,7 @@ export const UpdateTaskBody = z.object({
|
|
|
1582
1561
|
priority: z.enum(['low', 'medium', 'high']).optional(),
|
|
1583
1562
|
});
|
|
1584
1563
|
`);
|
|
1585
|
-
await
|
|
1564
|
+
await fs.outputFile(path.join(apiDir, "src", "data", "index.ts"), `export const users = [
|
|
1586
1565
|
{ id: '1', name: 'Alice', email: 'alice@example.com', role: 'admin' as const, createdAt: new Date().toISOString() },
|
|
1587
1566
|
{ id: '2', name: 'Bob', email: 'bob@example.com', role: 'user' as const, createdAt: new Date().toISOString() },
|
|
1588
1567
|
];
|
|
@@ -1598,7 +1577,7 @@ export const tasks = [
|
|
|
1598
1577
|
{ id: '3', title: 'Deploy', completed: false, priority: 'low' as const, createdAt: new Date().toISOString() },
|
|
1599
1578
|
];
|
|
1600
1579
|
`);
|
|
1601
|
-
await
|
|
1580
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "health", "get.ts"), `import { z } from 'zod';
|
|
1602
1581
|
|
|
1603
1582
|
export const schema = {
|
|
1604
1583
|
response: z.object({
|
|
@@ -1616,7 +1595,7 @@ export default async () => ({
|
|
|
1616
1595
|
uptime: process.uptime(),
|
|
1617
1596
|
});
|
|
1618
1597
|
`);
|
|
1619
|
-
await
|
|
1598
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "stats", "get.ts"), `import { z } from 'zod';
|
|
1620
1599
|
import { users, posts, tasks } from '../../../data/index.js';
|
|
1621
1600
|
|
|
1622
1601
|
export const schema = {
|
|
@@ -1637,7 +1616,7 @@ export default async () => ({
|
|
|
1637
1616
|
completedTasks: tasks.filter(t => t.completed).length,
|
|
1638
1617
|
});
|
|
1639
1618
|
`);
|
|
1640
|
-
await
|
|
1619
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "echo", "get.ts"), `import { z } from 'zod';
|
|
1641
1620
|
|
|
1642
1621
|
export const schema = {
|
|
1643
1622
|
query: z.object({ message: z.string() }),
|
|
@@ -1652,7 +1631,7 @@ export default async ({ query }: { query: { message: string } }) => ({
|
|
|
1652
1631
|
timestamp: new Date().toISOString(),
|
|
1653
1632
|
});
|
|
1654
1633
|
`);
|
|
1655
|
-
await
|
|
1634
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "validate", "post.ts"), `import { z } from 'zod';
|
|
1656
1635
|
|
|
1657
1636
|
export const schema = {
|
|
1658
1637
|
body: z.object({
|
|
@@ -1670,7 +1649,7 @@ export default async ({ body }: { body: { email: string; age: number } }) => ({
|
|
|
1670
1649
|
data: body,
|
|
1671
1650
|
});
|
|
1672
1651
|
`);
|
|
1673
|
-
await
|
|
1652
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "users", "get.ts"), `import { z } from 'zod';
|
|
1674
1653
|
import { users } from '../../../data/index.js';
|
|
1675
1654
|
import { UserSchema } from '../../../schemas/index.js';
|
|
1676
1655
|
|
|
@@ -1680,7 +1659,7 @@ export const schema = {
|
|
|
1680
1659
|
|
|
1681
1660
|
export default async () => users;
|
|
1682
1661
|
`);
|
|
1683
|
-
await
|
|
1662
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "users", "post.ts"), `import { users } from '../../../data/index.js';
|
|
1684
1663
|
import { UserSchema, CreateUserBody } from '../../../schemas/index.js';
|
|
1685
1664
|
|
|
1686
1665
|
export const schema = {
|
|
@@ -1700,7 +1679,7 @@ export default async ({ body }: { body: { name: string; email: string; role?: 'a
|
|
|
1700
1679
|
return newUser;
|
|
1701
1680
|
};
|
|
1702
1681
|
`);
|
|
1703
|
-
await
|
|
1682
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "users", "[id]", "get.ts"), `import { z } from 'zod';
|
|
1704
1683
|
import { KozoError } from '@kozojs/core';
|
|
1705
1684
|
import { users } from '../../../../data/index.js';
|
|
1706
1685
|
import { UserSchema } from '../../../../schemas/index.js';
|
|
@@ -1716,7 +1695,7 @@ export default async ({ params }: { params: { id: string } }) => {
|
|
|
1716
1695
|
return user;
|
|
1717
1696
|
};
|
|
1718
1697
|
`);
|
|
1719
|
-
await
|
|
1698
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "users", "[id]", "put.ts"), `import { z } from 'zod';
|
|
1720
1699
|
import { KozoError } from '@kozojs/core';
|
|
1721
1700
|
import { users } from '../../../../data/index.js';
|
|
1722
1701
|
import { UserSchema, UpdateUserBody } from '../../../../schemas/index.js';
|
|
@@ -1740,7 +1719,7 @@ export default async ({
|
|
|
1740
1719
|
return users[idx];
|
|
1741
1720
|
};
|
|
1742
1721
|
`);
|
|
1743
|
-
await
|
|
1722
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "users", "[id]", "delete.ts"), `import { z } from 'zod';
|
|
1744
1723
|
import { KozoError } from '@kozojs/core';
|
|
1745
1724
|
import { users } from '../../../../data/index.js';
|
|
1746
1725
|
|
|
@@ -1756,7 +1735,7 @@ export default async ({ params }: { params: { id: string } }) => {
|
|
|
1756
1735
|
return { success: true, message: 'User deleted' };
|
|
1757
1736
|
};
|
|
1758
1737
|
`);
|
|
1759
|
-
await
|
|
1738
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "posts", "get.ts"), `import { z } from 'zod';
|
|
1760
1739
|
import { posts } from '../../../data/index.js';
|
|
1761
1740
|
import { PostSchema } from '../../../schemas/index.js';
|
|
1762
1741
|
|
|
@@ -1772,7 +1751,7 @@ export default async ({ query }: { query: { published?: boolean } }) => {
|
|
|
1772
1751
|
return posts;
|
|
1773
1752
|
};
|
|
1774
1753
|
`);
|
|
1775
|
-
await
|
|
1754
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "posts", "post.ts"), `import { posts, users } from '../../../data/index.js';
|
|
1776
1755
|
import { PostSchema, CreatePostBody } from '../../../schemas/index.js';
|
|
1777
1756
|
|
|
1778
1757
|
export const schema = {
|
|
@@ -1798,7 +1777,7 @@ export default async ({
|
|
|
1798
1777
|
return newPost;
|
|
1799
1778
|
};
|
|
1800
1779
|
`);
|
|
1801
|
-
await
|
|
1780
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "posts", "[id]", "get.ts"), `import { z } from 'zod';
|
|
1802
1781
|
import { KozoError } from '@kozojs/core';
|
|
1803
1782
|
import { posts } from '../../../../data/index.js';
|
|
1804
1783
|
import { PostSchema } from '../../../../schemas/index.js';
|
|
@@ -1814,7 +1793,7 @@ export default async ({ params }: { params: { id: string } }) => {
|
|
|
1814
1793
|
return post;
|
|
1815
1794
|
};
|
|
1816
1795
|
`);
|
|
1817
|
-
await
|
|
1796
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "posts", "[id]", "put.ts"), `import { z } from 'zod';
|
|
1818
1797
|
import { KozoError } from '@kozojs/core';
|
|
1819
1798
|
import { posts } from '../../../../data/index.js';
|
|
1820
1799
|
import { PostSchema, UpdatePostBody } from '../../../../schemas/index.js';
|
|
@@ -1838,7 +1817,7 @@ export default async ({
|
|
|
1838
1817
|
return posts[idx];
|
|
1839
1818
|
};
|
|
1840
1819
|
`);
|
|
1841
|
-
await
|
|
1820
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "posts", "[id]", "delete.ts"), `import { z } from 'zod';
|
|
1842
1821
|
import { KozoError } from '@kozojs/core';
|
|
1843
1822
|
import { posts } from '../../../../data/index.js';
|
|
1844
1823
|
|
|
@@ -1854,7 +1833,7 @@ export default async ({ params }: { params: { id: string } }) => {
|
|
|
1854
1833
|
return { success: true, message: 'Post deleted' };
|
|
1855
1834
|
};
|
|
1856
1835
|
`);
|
|
1857
|
-
await
|
|
1836
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "tasks", "get.ts"), `import { z } from 'zod';
|
|
1858
1837
|
import { tasks } from '../../../data/index.js';
|
|
1859
1838
|
import { TaskSchema } from '../../../schemas/index.js';
|
|
1860
1839
|
|
|
@@ -1881,7 +1860,7 @@ export default async ({
|
|
|
1881
1860
|
return result;
|
|
1882
1861
|
};
|
|
1883
1862
|
`);
|
|
1884
|
-
await
|
|
1863
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "tasks", "post.ts"), `import { tasks } from '../../../data/index.js';
|
|
1885
1864
|
import { TaskSchema, CreateTaskBody } from '../../../schemas/index.js';
|
|
1886
1865
|
|
|
1887
1866
|
export const schema = {
|
|
@@ -1905,7 +1884,7 @@ export default async ({
|
|
|
1905
1884
|
return newTask;
|
|
1906
1885
|
};
|
|
1907
1886
|
`);
|
|
1908
|
-
await
|
|
1887
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "tasks", "[id]", "get.ts"), `import { z } from 'zod';
|
|
1909
1888
|
import { KozoError } from '@kozojs/core';
|
|
1910
1889
|
import { tasks } from '../../../../data/index.js';
|
|
1911
1890
|
import { TaskSchema } from '../../../../schemas/index.js';
|
|
@@ -1921,7 +1900,7 @@ export default async ({ params }: { params: { id: string } }) => {
|
|
|
1921
1900
|
return task;
|
|
1922
1901
|
};
|
|
1923
1902
|
`);
|
|
1924
|
-
await
|
|
1903
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "tasks", "[id]", "put.ts"), `import { z } from 'zod';
|
|
1925
1904
|
import { KozoError } from '@kozojs/core';
|
|
1926
1905
|
import { tasks } from '../../../../data/index.js';
|
|
1927
1906
|
import { TaskSchema, UpdateTaskBody } from '../../../../schemas/index.js';
|
|
@@ -1945,7 +1924,7 @@ export default async ({
|
|
|
1945
1924
|
return tasks[idx];
|
|
1946
1925
|
};
|
|
1947
1926
|
`);
|
|
1948
|
-
await
|
|
1927
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "tasks", "[id]", "delete.ts"), `import { z } from 'zod';
|
|
1949
1928
|
import { KozoError } from '@kozojs/core';
|
|
1950
1929
|
import { tasks } from '../../../../data/index.js';
|
|
1951
1930
|
|
|
@@ -1961,7 +1940,7 @@ export default async ({ params }: { params: { id: string } }) => {
|
|
|
1961
1940
|
return { success: true, message: 'Task deleted' };
|
|
1962
1941
|
};
|
|
1963
1942
|
`);
|
|
1964
|
-
await
|
|
1943
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "tasks", "[id]", "toggle", "patch.ts"), `import { z } from 'zod';
|
|
1965
1944
|
import { KozoError } from '@kozojs/core';
|
|
1966
1945
|
import { tasks } from '../../../../../data/index.js';
|
|
1967
1946
|
import { TaskSchema } from '../../../../../schemas/index.js';
|
|
@@ -1979,7 +1958,7 @@ export default async ({ params }: { params: { id: string } }) => {
|
|
|
1979
1958
|
};
|
|
1980
1959
|
`);
|
|
1981
1960
|
if (auth) {
|
|
1982
|
-
await
|
|
1961
|
+
await fs.outputFile(path.join(apiDir, "src", "routes", "api", "auth", "login", "post.ts"), `import { z } from 'zod';
|
|
1983
1962
|
import { createJWT, UnauthorizedError } from '@kozojs/auth';
|
|
1984
1963
|
|
|
1985
1964
|
const JWT_SECRET = process.env.JWT_SECRET || 'change-me';
|
|
@@ -2018,9 +1997,9 @@ export default async ({ body }: { body: { email: string; password: string } }) =
|
|
|
2018
1997
|
}
|
|
2019
1998
|
}
|
|
2020
1999
|
async function scaffoldFullstackWeb(projectDir, projectName, frontend, auth = false) {
|
|
2021
|
-
const webDir =
|
|
2022
|
-
await
|
|
2023
|
-
await
|
|
2000
|
+
const webDir = path.join(projectDir, "apps", "web");
|
|
2001
|
+
await fs.ensureDir(path.join(webDir, "src", "lib"));
|
|
2002
|
+
await fs.ensureDir(path.join(webDir, "src", "pages"));
|
|
2024
2003
|
const packageJson = {
|
|
2025
2004
|
name: `@${projectName}/web`,
|
|
2026
2005
|
version: "1.0.0",
|
|
@@ -2042,8 +2021,8 @@ async function scaffoldFullstackWeb(projectDir, projectName, frontend, auth = fa
|
|
|
2042
2021
|
tailwindcss: "^4.0.0"
|
|
2043
2022
|
}
|
|
2044
2023
|
};
|
|
2045
|
-
await
|
|
2046
|
-
await
|
|
2024
|
+
await fs.writeJSON(path.join(webDir, "package.json"), packageJson, { spaces: 2 });
|
|
2025
|
+
await fs.writeJSON(path.join(webDir, "tsconfig.json"), {
|
|
2047
2026
|
compilerOptions: {
|
|
2048
2027
|
target: "ES2020",
|
|
2049
2028
|
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
@@ -2061,7 +2040,7 @@ async function scaffoldFullstackWeb(projectDir, projectName, frontend, auth = fa
|
|
|
2061
2040
|
},
|
|
2062
2041
|
include: ["src"]
|
|
2063
2042
|
}, { spaces: 2 });
|
|
2064
|
-
await
|
|
2043
|
+
await fs.writeFile(path.join(webDir, "vite.config.ts"), `import { defineConfig } from 'vite';
|
|
2065
2044
|
import react from '@vitejs/plugin-react';
|
|
2066
2045
|
import tailwindcss from '@tailwindcss/vite';
|
|
2067
2046
|
|
|
@@ -2075,7 +2054,7 @@ export default defineConfig({
|
|
|
2075
2054
|
},
|
|
2076
2055
|
});
|
|
2077
2056
|
`);
|
|
2078
|
-
await
|
|
2057
|
+
await fs.writeFile(path.join(webDir, "index.html"), `<!DOCTYPE html>
|
|
2079
2058
|
<html lang="en">
|
|
2080
2059
|
<head>
|
|
2081
2060
|
<meta charset="UTF-8" />
|
|
@@ -2088,12 +2067,12 @@ export default defineConfig({
|
|
|
2088
2067
|
</body>
|
|
2089
2068
|
</html>
|
|
2090
2069
|
`);
|
|
2091
|
-
await
|
|
2070
|
+
await fs.writeFile(path.join(webDir, "src", "index.css"), `@import "tailwindcss";
|
|
2092
2071
|
|
|
2093
2072
|
body { background-color: rgb(15 23 42); color: rgb(241 245 249); font-family: ui-sans-serif, system-ui, sans-serif; }
|
|
2094
2073
|
* { box-sizing: border-box; }
|
|
2095
2074
|
`);
|
|
2096
|
-
await
|
|
2075
|
+
await fs.writeFile(path.join(webDir, "src", "lib", "api.ts"), `const API_BASE = '';
|
|
2097
2076
|
|
|
2098
2077
|
${auth ? `export function getToken(): string | null {
|
|
2099
2078
|
return localStorage.getItem('kozo_token');
|
|
@@ -2130,7 +2109,7 @@ ${auth ? ` const token = getToken();
|
|
|
2130
2109
|
return { data, status: res.status, ms: Math.round(performance.now() - start) };
|
|
2131
2110
|
}
|
|
2132
2111
|
`);
|
|
2133
|
-
await
|
|
2112
|
+
await fs.writeFile(path.join(webDir, "src", "main.tsx"), `import React from 'react';
|
|
2134
2113
|
import ReactDOM from 'react-dom/client';
|
|
2135
2114
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2136
2115
|
import App from './App';
|
|
@@ -2148,14 +2127,14 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
2148
2127
|
</React.StrictMode>
|
|
2149
2128
|
);
|
|
2150
2129
|
`);
|
|
2151
|
-
await
|
|
2152
|
-
await
|
|
2153
|
-
await
|
|
2154
|
-
await
|
|
2130
|
+
await fs.writeFile(path.join(webDir, "src", "pages", "Dashboard.tsx"), generateDashboardPage());
|
|
2131
|
+
await fs.writeFile(path.join(webDir, "src", "pages", "Users.tsx"), generateUsersPage());
|
|
2132
|
+
await fs.writeFile(path.join(webDir, "src", "pages", "Posts.tsx"), generatePostsPage());
|
|
2133
|
+
await fs.writeFile(path.join(webDir, "src", "pages", "Tasks.tsx"), generateTasksPage());
|
|
2155
2134
|
if (auth) {
|
|
2156
|
-
await
|
|
2135
|
+
await fs.writeFile(path.join(webDir, "src", "pages", "Login.tsx"), generateLoginPage());
|
|
2157
2136
|
}
|
|
2158
|
-
await
|
|
2137
|
+
await fs.writeFile(path.join(webDir, "src", "App.tsx"), generateAppTsx(projectName, auth));
|
|
2159
2138
|
}
|
|
2160
2139
|
function generateAppTsx(projectName, auth) {
|
|
2161
2140
|
const authImports = auth ? `import { getToken, clearToken } from './lib/api';` : "";
|
|
@@ -2939,19 +2918,19 @@ const res = await client.api.users.$get();
|
|
|
2939
2918
|
const users = await res.json(); // Fully typed!
|
|
2940
2919
|
\`\`\`
|
|
2941
2920
|
`;
|
|
2942
|
-
await
|
|
2921
|
+
await fs.writeFile(path.join(projectDir, "README.md"), readme);
|
|
2943
2922
|
}
|
|
2944
2923
|
|
|
2945
2924
|
// src/utils/ascii-art.ts
|
|
2946
|
-
|
|
2925
|
+
import pc from "picocolors";
|
|
2947
2926
|
var KOZO_LOGO = `
|
|
2948
|
-
${
|
|
2949
|
-
${
|
|
2950
|
-
${
|
|
2951
|
-
${
|
|
2927
|
+
${pc.red(" _ __")}${pc.yellow("___ ")}${pc.red("______")}${pc.yellow("___ ")}
|
|
2928
|
+
${pc.red("| |/ /")}${pc.yellow(" _ \\\\")}${pc.red("|_ /")}${pc.yellow(" _ \\\\")}
|
|
2929
|
+
${pc.red("| ' /")}${pc.yellow(" (_) |")}${pc.red("/ /")}${pc.yellow(" (_) |")}
|
|
2930
|
+
${pc.red("|_|\\_\\\\")}${pc.yellow("___/")}${pc.red("___|\\\\")}${pc.yellow("___/")}
|
|
2952
2931
|
`;
|
|
2953
2932
|
var KOZO_BANNER = `
|
|
2954
|
-
${
|
|
2933
|
+
${pc.bold(pc.red("\u{1F525} KOZO"))} ${pc.dim("- The Structure for the Edge")}
|
|
2955
2934
|
`;
|
|
2956
2935
|
function printLogo() {
|
|
2957
2936
|
console.log(KOZO_LOGO);
|
|
@@ -2960,7 +2939,7 @@ function printLogo() {
|
|
|
2960
2939
|
// src/commands/new.ts
|
|
2961
2940
|
async function newCommand(projectName) {
|
|
2962
2941
|
printLogo();
|
|
2963
|
-
p.intro(
|
|
2942
|
+
p.intro(pc2.bold(pc2.red("\u{1F525} Create a new Kozo project")));
|
|
2964
2943
|
const isLocalWorkspace = process.env.KOZO_LOCAL === "true";
|
|
2965
2944
|
const project = await p.group(
|
|
2966
2945
|
{
|
|
@@ -3078,14 +3057,14 @@ async function newCommand(projectName) {
|
|
|
3078
3057
|
if (project.install) {
|
|
3079
3058
|
s.start("Installing dependencies...");
|
|
3080
3059
|
try {
|
|
3081
|
-
await
|
|
3060
|
+
await execa("pnpm", ["install"], {
|
|
3082
3061
|
cwd: project.name,
|
|
3083
3062
|
stdio: "pipe"
|
|
3084
3063
|
});
|
|
3085
3064
|
s.stop("Dependencies installed!");
|
|
3086
3065
|
} catch {
|
|
3087
3066
|
try {
|
|
3088
|
-
await
|
|
3067
|
+
await execa("npm", ["install"], {
|
|
3089
3068
|
cwd: project.name,
|
|
3090
3069
|
stdio: "pipe"
|
|
3091
3070
|
});
|
|
@@ -3096,33 +3075,33 @@ async function newCommand(projectName) {
|
|
|
3096
3075
|
}
|
|
3097
3076
|
}
|
|
3098
3077
|
}
|
|
3099
|
-
p.outro(
|
|
3078
|
+
p.outro(pc2.green("\u2728 Project ready!"));
|
|
3100
3079
|
console.log(`
|
|
3101
|
-
${
|
|
3080
|
+
${pc2.bold("Next steps:")}
|
|
3102
3081
|
|
|
3103
|
-
${
|
|
3104
|
-
${!project.install ?
|
|
3082
|
+
${pc2.cyan(`cd ${project.name}`)}
|
|
3083
|
+
${!project.install ? pc2.cyan("pnpm install") + "\n " : ""}${pc2.cyan("pnpm dev")}
|
|
3105
3084
|
|
|
3106
|
-
${
|
|
3085
|
+
${pc2.dim("Documentation:")} ${pc2.underline("https://kozo-docs.vercel.app")}
|
|
3107
3086
|
`);
|
|
3108
3087
|
}
|
|
3109
3088
|
|
|
3110
3089
|
// src/commands/build.ts
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3090
|
+
import { execa as execa2 } from "execa";
|
|
3091
|
+
import pc3 from "picocolors";
|
|
3092
|
+
import fs2 from "fs-extra";
|
|
3093
|
+
import path2 from "path";
|
|
3115
3094
|
|
|
3116
3095
|
// src/routing/manifest.ts
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3096
|
+
import { createHash } from "crypto";
|
|
3097
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
3098
|
+
import { join as join2, dirname } from "path";
|
|
3099
|
+
import { glob as glob2 } from "glob";
|
|
3121
3100
|
|
|
3122
3101
|
// src/routing/scan.ts
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3102
|
+
import { glob } from "glob";
|
|
3103
|
+
import { join } from "path";
|
|
3104
|
+
import { readFileSync } from "fs";
|
|
3126
3105
|
var HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
3127
3106
|
function fileToRoute(filePath) {
|
|
3128
3107
|
const normalized = filePath.replace(/\\/g, "/");
|
|
@@ -3160,7 +3139,7 @@ function isRouteFile(file) {
|
|
|
3160
3139
|
function detectSchemas(absolutePath) {
|
|
3161
3140
|
let source = "";
|
|
3162
3141
|
try {
|
|
3163
|
-
source =
|
|
3142
|
+
source = readFileSync(absolutePath, "utf8");
|
|
3164
3143
|
} catch {
|
|
3165
3144
|
return { hasBodySchema: false, hasQuerySchema: false };
|
|
3166
3145
|
}
|
|
@@ -3180,7 +3159,7 @@ function routeScore(urlPath) {
|
|
|
3180
3159
|
}
|
|
3181
3160
|
async function scanRoutes(options) {
|
|
3182
3161
|
const { routesDir, verbose = false } = options;
|
|
3183
|
-
const files = await
|
|
3162
|
+
const files = await glob("**/*.{ts,js}", {
|
|
3184
3163
|
cwd: routesDir,
|
|
3185
3164
|
nodir: true,
|
|
3186
3165
|
ignore: ["**/_*.ts", "**/_*.js", "**/*.test.ts", "**/*.spec.ts", "**/*.test.js", "**/*.spec.js"]
|
|
@@ -3190,7 +3169,7 @@ async function scanRoutes(options) {
|
|
|
3190
3169
|
if (!isRouteFile(file)) continue;
|
|
3191
3170
|
const parsed = fileToRoute(file);
|
|
3192
3171
|
if (!parsed) continue;
|
|
3193
|
-
const absolutePath =
|
|
3172
|
+
const absolutePath = join(routesDir, file);
|
|
3194
3173
|
const { hasBodySchema, hasQuerySchema } = detectSchemas(absolutePath);
|
|
3195
3174
|
const params = extractParams(parsed.path);
|
|
3196
3175
|
routes.push({
|
|
@@ -3215,17 +3194,17 @@ async function scanRoutes(options) {
|
|
|
3215
3194
|
|
|
3216
3195
|
// src/routing/manifest.ts
|
|
3217
3196
|
async function hashRouteFiles(routesDir) {
|
|
3218
|
-
const files = await (
|
|
3197
|
+
const files = await glob2("**/*.{ts,js}", {
|
|
3219
3198
|
cwd: routesDir,
|
|
3220
3199
|
nodir: true,
|
|
3221
3200
|
ignore: ["**/_*.ts", "**/_*.js", "**/*.test.ts", "**/*.spec.ts", "**/*.test.js", "**/*.spec.js"]
|
|
3222
3201
|
});
|
|
3223
3202
|
files.sort();
|
|
3224
|
-
const hash =
|
|
3203
|
+
const hash = createHash("sha256");
|
|
3225
3204
|
for (const file of files) {
|
|
3226
3205
|
hash.update(file);
|
|
3227
3206
|
try {
|
|
3228
|
-
const content = (
|
|
3207
|
+
const content = readFileSync2(join2(routesDir, file));
|
|
3229
3208
|
hash.update(content);
|
|
3230
3209
|
} catch {
|
|
3231
3210
|
}
|
|
@@ -3233,9 +3212,9 @@ async function hashRouteFiles(routesDir) {
|
|
|
3233
3212
|
return hash.digest("hex");
|
|
3234
3213
|
}
|
|
3235
3214
|
function readExistingManifest(manifestPath) {
|
|
3236
|
-
if (!
|
|
3215
|
+
if (!existsSync(manifestPath)) return null;
|
|
3237
3216
|
try {
|
|
3238
|
-
const raw = (
|
|
3217
|
+
const raw = readFileSync2(manifestPath, "utf8");
|
|
3239
3218
|
return JSON.parse(raw);
|
|
3240
3219
|
} catch {
|
|
3241
3220
|
return null;
|
|
@@ -3245,7 +3224,7 @@ async function generateManifest(options) {
|
|
|
3245
3224
|
const {
|
|
3246
3225
|
routesDir,
|
|
3247
3226
|
projectRoot,
|
|
3248
|
-
outputPath = (
|
|
3227
|
+
outputPath = join2(projectRoot, "routes-manifest.json"),
|
|
3249
3228
|
cache = true,
|
|
3250
3229
|
verbose = false
|
|
3251
3230
|
} = options;
|
|
@@ -3278,11 +3257,11 @@ async function generateManifest(options) {
|
|
|
3278
3257
|
contentHash,
|
|
3279
3258
|
routes: entries
|
|
3280
3259
|
};
|
|
3281
|
-
const dir =
|
|
3282
|
-
if (!
|
|
3283
|
-
|
|
3260
|
+
const dir = dirname(outputPath);
|
|
3261
|
+
if (!existsSync(dir)) {
|
|
3262
|
+
mkdirSync(dir, { recursive: true });
|
|
3284
3263
|
}
|
|
3285
|
-
|
|
3264
|
+
writeFileSync(outputPath, JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
3286
3265
|
if (verbose) {
|
|
3287
3266
|
console.log(` \u2713 Generated routes-manifest.json (${entries.length} routes, hash: ${contentHash.slice(0, 8)}\u2026)`);
|
|
3288
3267
|
}
|
|
@@ -3295,22 +3274,22 @@ function printBox(title) {
|
|
|
3295
3274
|
const width = 50;
|
|
3296
3275
|
const pad = Math.max(0, Math.floor((width - title.length) / 2));
|
|
3297
3276
|
const line = "\u2500".repeat(width);
|
|
3298
|
-
console.log(
|
|
3299
|
-
console.log(
|
|
3300
|
-
console.log(
|
|
3277
|
+
console.log(pc3.cyan(`\u250C${line}\u2510`));
|
|
3278
|
+
console.log(pc3.cyan("\u2502") + " ".repeat(pad) + pc3.bold(title) + " ".repeat(width - pad - title.length) + pc3.cyan("\u2502"));
|
|
3279
|
+
console.log(pc3.cyan(`\u2514${line}\u2518`));
|
|
3301
3280
|
console.log();
|
|
3302
3281
|
}
|
|
3303
3282
|
function step(n, total, label) {
|
|
3304
|
-
console.log(
|
|
3283
|
+
console.log(pc3.dim(`[${n}/${total}]`) + " " + pc3.cyan("\u2192") + " " + label);
|
|
3305
3284
|
}
|
|
3306
3285
|
function ok(label) {
|
|
3307
|
-
console.log(
|
|
3286
|
+
console.log(pc3.green(" \u2713") + " " + label);
|
|
3308
3287
|
}
|
|
3309
3288
|
function fail(label, err) {
|
|
3310
|
-
console.log(
|
|
3289
|
+
console.log(pc3.red(" \u2717") + " " + label);
|
|
3311
3290
|
if (err) {
|
|
3312
3291
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3313
|
-
console.log(
|
|
3292
|
+
console.log(pc3.dim(" " + msg));
|
|
3314
3293
|
}
|
|
3315
3294
|
}
|
|
3316
3295
|
async function buildCommand(options = {}) {
|
|
@@ -3321,11 +3300,11 @@ async function buildCommand(options = {}) {
|
|
|
3321
3300
|
let currentStep = 0;
|
|
3322
3301
|
currentStep++;
|
|
3323
3302
|
step(currentStep, TOTAL_STEPS, "Checking project structure\u2026");
|
|
3324
|
-
if (!
|
|
3303
|
+
if (!fs2.existsSync(path2.join(cwd, "package.json"))) {
|
|
3325
3304
|
fail("No package.json found. Run this command inside a Kozo project.");
|
|
3326
3305
|
process.exit(1);
|
|
3327
3306
|
}
|
|
3328
|
-
if (!
|
|
3307
|
+
if (!fs2.existsSync(path2.join(cwd, "node_modules"))) {
|
|
3329
3308
|
fail("Dependencies not installed. Run `npm install` first.");
|
|
3330
3309
|
process.exit(1);
|
|
3331
3310
|
}
|
|
@@ -3333,7 +3312,7 @@ async function buildCommand(options = {}) {
|
|
|
3333
3312
|
currentStep++;
|
|
3334
3313
|
step(currentStep, TOTAL_STEPS, "Cleaning previous build\u2026");
|
|
3335
3314
|
try {
|
|
3336
|
-
await
|
|
3315
|
+
await fs2.remove(path2.join(cwd, "dist"));
|
|
3337
3316
|
ok("dist/ cleaned");
|
|
3338
3317
|
} catch (err) {
|
|
3339
3318
|
fail("Failed to clean dist/", err);
|
|
@@ -3343,12 +3322,12 @@ async function buildCommand(options = {}) {
|
|
|
3343
3322
|
currentStep++;
|
|
3344
3323
|
step(currentStep, TOTAL_STEPS, "Generating routes manifest\u2026");
|
|
3345
3324
|
const routesDirRel = options.routesDir ?? "src/routes";
|
|
3346
|
-
const routesDirAbs =
|
|
3347
|
-
if (!
|
|
3348
|
-
console.log(
|
|
3325
|
+
const routesDirAbs = path2.join(cwd, routesDirRel);
|
|
3326
|
+
if (!fs2.existsSync(routesDirAbs)) {
|
|
3327
|
+
console.log(pc3.dim(` \u26A0 Routes directory not found (${routesDirRel}), skipping manifest.`));
|
|
3349
3328
|
} else {
|
|
3350
3329
|
try {
|
|
3351
|
-
const manifestOutAbs = options.manifestOut ?
|
|
3330
|
+
const manifestOutAbs = options.manifestOut ? path2.join(cwd, options.manifestOut) : path2.join(cwd, "routes-manifest.json");
|
|
3352
3331
|
const manifest = await generateManifest({
|
|
3353
3332
|
routesDir: routesDirAbs,
|
|
3354
3333
|
projectRoot: cwd,
|
|
@@ -3359,7 +3338,7 @@ async function buildCommand(options = {}) {
|
|
|
3359
3338
|
ok(`Manifest ready \u2014 ${manifest.routes.length} route(s)`);
|
|
3360
3339
|
} catch (err) {
|
|
3361
3340
|
fail("Manifest generation failed", err);
|
|
3362
|
-
console.log(
|
|
3341
|
+
console.log(pc3.dim(" Continuing build without manifest\u2026"));
|
|
3363
3342
|
}
|
|
3364
3343
|
}
|
|
3365
3344
|
}
|
|
@@ -3367,7 +3346,7 @@ async function buildCommand(options = {}) {
|
|
|
3367
3346
|
step(currentStep, TOTAL_STEPS, "Compiling with tsup\u2026");
|
|
3368
3347
|
try {
|
|
3369
3348
|
const tsupArgs = ["tsup", ...options.tsupArgs ?? []];
|
|
3370
|
-
await (
|
|
3349
|
+
await execa2("npx", tsupArgs, {
|
|
3371
3350
|
cwd,
|
|
3372
3351
|
stdio: "inherit",
|
|
3373
3352
|
env: { ...process.env, NODE_ENV: "production" }
|
|
@@ -3378,12 +3357,12 @@ async function buildCommand(options = {}) {
|
|
|
3378
3357
|
process.exit(1);
|
|
3379
3358
|
}
|
|
3380
3359
|
console.log();
|
|
3381
|
-
console.log(
|
|
3360
|
+
console.log(pc3.green("\u2705 Build successful"));
|
|
3382
3361
|
console.log();
|
|
3383
3362
|
}
|
|
3384
3363
|
|
|
3385
3364
|
// src/index.ts
|
|
3386
|
-
var program = new
|
|
3365
|
+
var program = new Command();
|
|
3387
3366
|
program.name("kozo").description("CLI to scaffold new Kozo Framework projects").version("0.2.6");
|
|
3388
3367
|
program.argument("[project-name]", "Name of the project").action(async (projectName) => {
|
|
3389
3368
|
await newCommand(projectName);
|
package/package.json
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@kozojs/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI to scaffold new Kozo Framework projects - The next-gen TypeScript Backend Framework",
|
|
5
|
-
"bin": {
|
|
6
|
-
"create-kozo": "./lib/index.js",
|
|
7
|
-
"kozo": "./lib/index.js"
|
|
8
|
-
},
|
|
9
|
-
"main": "./lib/index.js",
|
|
10
|
-
"types": "./lib/index.d.ts",
|
|
11
|
-
"files": [
|
|
12
|
-
"lib",
|
|
13
|
-
"README.md"
|
|
14
|
-
],
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@kozojs/cli",
|
|
3
|
+
"version": "0.1.22",
|
|
4
|
+
"description": "CLI to scaffold new Kozo Framework projects - The next-gen TypeScript Backend Framework",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-kozo": "./lib/index.js",
|
|
7
|
+
"kozo": "./lib/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./lib/index.js",
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"lib",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"kozo",
|
|
21
|
+
"framework",
|
|
22
|
+
"backend",
|
|
23
|
+
"typescript",
|
|
24
|
+
"hono",
|
|
25
|
+
"api",
|
|
26
|
+
"rest",
|
|
27
|
+
"cli",
|
|
28
|
+
"scaffold",
|
|
29
|
+
"generator"
|
|
30
|
+
],
|
|
31
|
+
"author": "Kozo Team",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@clack/prompts": "^0.8.0",
|
|
38
|
+
"commander": "^12.0.0",
|
|
39
|
+
"picocolors": "^1.1.0",
|
|
40
|
+
"ora": "^8.1.0",
|
|
41
|
+
"execa": "^9.5.0",
|
|
42
|
+
"fs-extra": "^11.2.0",
|
|
43
|
+
"glob": "^11.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/fs-extra": "^11.0.4",
|
|
47
|
+
"@types/node": "^22.0.0",
|
|
48
|
+
"tsup": "^8.3.0",
|
|
49
|
+
"typescript": "^5.6.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/lib/index.d.ts
DELETED