@kozojs/cli 0.1.11 → 0.1.13

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.
Files changed (2) hide show
  1. package/lib/index.js +165 -18
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -39,11 +39,12 @@ async function scaffoldProject(options) {
39
39
  const projectDir = import_node_path.default.resolve(process.cwd(), projectName);
40
40
  const kozoCoreDep = packageSource === "local" ? "workspace:*" : "^0.2.0";
41
41
  if (frontend !== "none") {
42
- await scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, runtime, database, frontend, extras, template);
42
+ await scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, runtime, database, dbPort, auth, frontend, extras, template);
43
43
  return;
44
44
  }
45
45
  if (template === "complete") {
46
46
  await scaffoldCompleteTemplate(projectDir, projectName, kozoCoreDep, runtime, database, dbPort, auth);
47
+ if (database !== "none" && database !== "sqlite") await createDockerCompose(projectDir, projectName, database, dbPort);
47
48
  if (extras.includes("docker")) await createDockerfile(projectDir, runtime);
48
49
  if (extras.includes("github-actions")) await createGitHubActions(projectDir);
49
50
  return;
@@ -250,6 +251,9 @@ ${database === "sqlite" ? "## SQLite Notes\n\nThe database is automatically init
250
251
  - [Hono](https://hono.dev)
251
252
  `;
252
253
  await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, "README.md"), readme);
254
+ if (database !== "none" && database !== "sqlite") await createDockerCompose(projectDir, projectName, database, dbPort);
255
+ if (extras.includes("docker")) await createDockerfile(projectDir, runtime);
256
+ if (extras.includes("github-actions")) await createGitHubActions(projectDir);
253
257
  }
254
258
  function getDatabaseSchema(database) {
255
259
  if (database === "postgresql") {
@@ -1238,12 +1242,92 @@ await app.nativeListen();
1238
1242
  await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, "src", "index.ts"), indexTs);
1239
1243
  await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, ".gitignore"), "node_modules/\ndist/\n.env\n");
1240
1244
  }
1245
+ async function createDockerCompose(dir, projectName, database, dbPort, includeApiService = false, runtime = "node") {
1246
+ if (database === "none" || database === "sqlite") return;
1247
+ const pgPort = dbPort ?? 5436;
1248
+ let services = "";
1249
+ if (database === "postgresql") {
1250
+ const dbUrl = `postgresql://postgres:postgres@db:5432/${projectName}`;
1251
+ services += ` db:
1252
+ image: postgres:16-alpine
1253
+ restart: unless-stopped
1254
+ ports:
1255
+ - "${pgPort}:5432"
1256
+ environment:
1257
+ POSTGRES_USER: postgres
1258
+ POSTGRES_PASSWORD: postgres
1259
+ POSTGRES_DB: ${projectName}
1260
+ volumes:
1261
+ - postgres_data:/var/lib/postgresql/data
1262
+ healthcheck:
1263
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
1264
+ interval: 5s
1265
+ timeout: 5s
1266
+ retries: 5
1267
+ `;
1268
+ if (includeApiService) {
1269
+ const runCmd = runtime === "bun" ? "bun dist/index.js" : "node dist/index.js";
1270
+ services += `
1271
+ api:
1272
+ build: .
1273
+ ports:
1274
+ - "3000:3000"
1275
+ environment:
1276
+ NODE_ENV: production
1277
+ DATABASE_URL: ${dbUrl}
1278
+ JWT_SECRET: \${JWT_SECRET:-change-me-in-production}
1279
+ depends_on:
1280
+ db:
1281
+ condition: service_healthy
1282
+ command: ${runCmd}
1283
+ `;
1284
+ }
1285
+ } else if (database === "mysql") {
1286
+ const dbUrl = `mysql://root:root@db:3306/${projectName}`;
1287
+ services += ` db:
1288
+ image: mysql:8-alpine
1289
+ restart: unless-stopped
1290
+ ports:
1291
+ - "3306:3306"
1292
+ environment:
1293
+ MYSQL_ROOT_PASSWORD: root
1294
+ MYSQL_DATABASE: ${projectName}
1295
+ volumes:
1296
+ - mysql_data:/var/lib/mysql
1297
+ healthcheck:
1298
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
1299
+ interval: 5s
1300
+ timeout: 5s
1301
+ retries: 5
1302
+ `;
1303
+ if (includeApiService) {
1304
+ const runCmd = runtime === "bun" ? "bun dist/index.js" : "node dist/index.js";
1305
+ services += `
1306
+ api:
1307
+ build: .
1308
+ ports:
1309
+ - "3000:3000"
1310
+ environment:
1311
+ NODE_ENV: production
1312
+ DATABASE_URL: ${dbUrl}
1313
+ JWT_SECRET: \${JWT_SECRET:-change-me-in-production}
1314
+ depends_on:
1315
+ db:
1316
+ condition: service_healthy
1317
+ command: ${runCmd}
1318
+ `;
1319
+ }
1320
+ }
1321
+ const volumes = database === "postgresql" ? "\nvolumes:\n postgres_data:\n" : database === "mysql" ? "\nvolumes:\n mysql_data:\n" : "";
1322
+ const compose = `services:
1323
+ ${services}${volumes}`;
1324
+ await import_fs_extra.default.writeFile(import_node_path.default.join(dir, "docker-compose.yml"), compose);
1325
+ }
1241
1326
  async function createDockerfile(projectDir, runtime) {
1242
- const dockerfile = runtime === "bun" ? `FROM oven/bun:1 as builder
1327
+ const dockerfile = runtime === "bun" ? `FROM oven/bun:1 AS builder
1243
1328
  WORKDIR /app
1244
1329
  COPY package.json bun.lockb* ./
1245
1330
  RUN bun install --frozen-lockfile
1246
-
1247
1331
  COPY . .
1248
1332
  RUN bun run build
1249
1333
 
@@ -1253,11 +1337,10 @@ COPY --from=builder /app/dist ./dist
1253
1337
  COPY --from=builder /app/package.json ./
1254
1338
  EXPOSE 3000
1255
1339
  CMD ["bun", "dist/index.js"]
1256
- ` : `FROM node:20-alpine as builder
1340
+ ` : `FROM node:20-alpine AS builder
1257
1341
  WORKDIR /app
1258
1342
  COPY package*.json ./
1259
1343
  RUN npm ci
1260
-
1261
1344
  COPY . .
1262
1345
  RUN npm run build
1263
1346
 
@@ -1270,7 +1353,7 @@ EXPOSE 3000
1270
1353
  CMD ["node", "dist/index.js"]
1271
1354
  `;
1272
1355
  await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, "Dockerfile"), dockerfile);
1273
- await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, ".dockerignore"), "node_modules\ndist\n.git\n");
1356
+ await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, ".dockerignore"), "node_modules\ndist\n.git\n.env\n");
1274
1357
  }
1275
1358
  async function createGitHubActions(projectDir) {
1276
1359
  await import_fs_extra.default.ensureDir(import_node_path.default.join(projectDir, ".github", "workflows"));
@@ -1297,9 +1380,11 @@ jobs:
1297
1380
  `;
1298
1381
  await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, ".github", "workflows", "ci.yml"), workflow);
1299
1382
  }
1300
- async function scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, runtime, database, frontend, extras, template) {
1383
+ async function scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, runtime, database, dbPort, auth, frontend, extras, template) {
1384
+ const hasDb = database !== "none";
1301
1385
  await import_fs_extra.default.ensureDir(import_node_path.default.join(projectDir, "apps", "api", "src", "routes"));
1302
- await import_fs_extra.default.ensureDir(import_node_path.default.join(projectDir, "apps", "api", "src", "data"));
1386
+ if (!hasDb) await import_fs_extra.default.ensureDir(import_node_path.default.join(projectDir, "apps", "api", "src", "data"));
1387
+ if (hasDb) await import_fs_extra.default.ensureDir(import_node_path.default.join(projectDir, "apps", "api", "src", "db"));
1303
1388
  await import_fs_extra.default.ensureDir(import_node_path.default.join(projectDir, "apps", "web", "src", "lib"));
1304
1389
  await import_fs_extra.default.ensureDir(import_node_path.default.join(projectDir, ".vscode"));
1305
1390
  const rootPackageJson = {
@@ -1315,32 +1400,82 @@ async function scaffoldFullstackProject(projectDir, projectName, kozoCoreDep, ru
1315
1400
  - 'apps/*'
1316
1401
  `);
1317
1402
  await import_fs_extra.default.writeFile(import_node_path.default.join(projectDir, ".gitignore"), "node_modules/\ndist/\n.env\n*.log\n");
1318
- await scaffoldFullstackApi(projectDir, projectName, kozoCoreDep, runtime);
1403
+ await scaffoldFullstackApi(projectDir, projectName, kozoCoreDep, runtime, database, dbPort, auth);
1319
1404
  await scaffoldFullstackWeb(projectDir, projectName, frontend);
1320
1405
  await scaffoldFullstackReadme(projectDir, projectName);
1406
+ if (database !== "none" && database !== "sqlite") await createDockerCompose(projectDir, projectName, database, dbPort);
1321
1407
  if (extras.includes("docker")) await createDockerfile(import_node_path.default.join(projectDir, "apps", "api"), runtime);
1322
1408
  if (extras.includes("github-actions")) await createGitHubActions(projectDir);
1323
1409
  }
1324
- async function scaffoldFullstackApi(projectDir, projectName, kozoCoreDep, runtime) {
1410
+ async function scaffoldFullstackApi(projectDir, projectName, kozoCoreDep, runtime, database = "none", dbPort, auth = true) {
1325
1411
  const apiDir = import_node_path.default.join(projectDir, "apps", "api");
1412
+ const hasDb = database !== "none";
1413
+ if (hasDb) {
1414
+ await import_fs_extra.default.ensureDir(import_node_path.default.join(apiDir, "src", "db"));
1415
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "db", "schema.ts"), getDatabaseSchema(database));
1416
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "db", "index.ts"), getDatabaseIndex(database));
1417
+ if (database === "sqlite") {
1418
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "db", "seed.ts"), getSQLiteSeed());
1419
+ }
1420
+ const dialect = database === "postgresql" ? "postgresql" : database === "mysql" ? "mysql" : "sqlite";
1421
+ const pgPort = dbPort ?? 5436;
1422
+ const dbUrl = database === "postgresql" ? `postgresql://postgres:postgres@localhost:${pgPort}/${projectName}` : database === "mysql" ? `mysql://root:root@localhost:3306/${projectName}` : void 0;
1423
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "drizzle.config.ts"), `import { defineConfig } from 'drizzle-kit';
1424
+ import 'dotenv/config';
1425
+
1426
+ export default defineConfig({
1427
+ schema: './src/db/schema.ts',
1428
+ out: './drizzle',
1429
+ dialect: '${dialect}',
1430
+ dbCredentials: {
1431
+ ${database === "sqlite" ? "url: './data.db'" : "url: process.env.DATABASE_URL!"}
1432
+ },
1433
+ });
1434
+ `);
1435
+ const envContent = `PORT=3000
1436
+ NODE_ENV=development
1437
+ ${dbUrl ? `DATABASE_URL=${dbUrl}
1438
+ ` : ""}${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
1439
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, ".env"), envContent);
1440
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, ".env.example"), envContent);
1441
+ } else {
1442
+ const envContent = `PORT=3000
1443
+ NODE_ENV=development
1444
+ ${auth ? "JWT_SECRET=change-me-to-a-random-secret-at-least-32-chars\n" : ""}`;
1445
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, ".env"), envContent);
1446
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, ".env.example"), envContent);
1447
+ }
1326
1448
  const apiPackageJson = {
1327
1449
  name: `@${projectName}/api`,
1328
1450
  version: "1.0.0",
1329
1451
  type: "module",
1330
1452
  scripts: {
1331
1453
  dev: runtime === "bun" ? "bun --watch src/index.ts" : "tsx watch src/index.ts",
1332
- build: "tsc"
1454
+ build: "tsc",
1455
+ ...hasDb && {
1456
+ "db:generate": "drizzle-kit generate",
1457
+ "db:push": "drizzle-kit push",
1458
+ "db:studio": "drizzle-kit studio"
1459
+ }
1333
1460
  },
1334
1461
  dependencies: {
1335
1462
  "@kozojs/core": kozoCoreDep,
1463
+ ...auth && { "@kozojs/auth": kozoCoreDep },
1336
1464
  hono: "^4.6.0",
1337
1465
  zod: "^3.23.0",
1338
- ...runtime === "node" && { "@hono/node-server": "^1.13.0" }
1466
+ dotenv: "^16.4.0",
1467
+ ...runtime === "node" && { "@hono/node-server": "^1.13.0" },
1468
+ ...hasDb && { "drizzle-orm": "^0.36.0" },
1469
+ ...database === "postgresql" && { postgres: "^3.4.0" },
1470
+ ...database === "mysql" && { mysql2: "^3.11.0" },
1471
+ ...database === "sqlite" && { "better-sqlite3": "^11.0.0" }
1339
1472
  },
1340
1473
  devDependencies: {
1341
1474
  "@types/node": "^22.0.0",
1342
1475
  ...runtime !== "bun" && { tsx: "^4.19.0" },
1343
- typescript: "^5.6.0"
1476
+ typescript: "^5.6.0",
1477
+ ...hasDb && { "drizzle-kit": "^0.28.0" },
1478
+ ...database === "sqlite" && { "@types/better-sqlite3": "^7.6.0" }
1344
1479
  }
1345
1480
  };
1346
1481
  await import_fs_extra.default.writeJSON(import_node_path.default.join(apiDir, "package.json"), apiPackageJson, { spaces: 2 });
@@ -1359,11 +1494,23 @@ async function scaffoldFullstackApi(projectDir, projectName, kozoCoreDep, runtim
1359
1494
  exclude: ["node_modules", "dist"]
1360
1495
  };
1361
1496
  await import_fs_extra.default.writeJSON(import_node_path.default.join(apiDir, "tsconfig.json"), tsconfig, { spaces: 2 });
1362
- await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "index.ts"), `import { createKozo } from '@kozojs/core';
1363
- import { registerRoutes } from './routes';
1364
-
1365
- const app = createKozo({ port: 3000 });
1366
-
1497
+ const dbImport = hasDb ? `import { createDb } from './db/index.js';
1498
+ ` : "";
1499
+ const authImport = auth ? `import { authenticateJWT } from '@kozojs/auth';
1500
+ ` : "";
1501
+ const servicesSetup = hasDb ? `
1502
+ const db = await createDb();
1503
+ const services = { db };
1504
+ ` : "\nconst services = {};\n";
1505
+ const authMiddleware = auth ? `
1506
+ app.use('*', authenticateJWT({ secret: process.env.JWT_SECRET || 'change-me' }));
1507
+ ` : "";
1508
+ await import_fs_extra.default.writeFile(import_node_path.default.join(apiDir, "src", "index.ts"), `import 'dotenv/config';
1509
+ import { createKozo } from '@kozojs/core';
1510
+ ${dbImport}${authImport}import { registerRoutes } from './routes/index.js';
1511
+ ${servicesSetup}
1512
+ const app = createKozo({ port: 3000, services });
1513
+ ${authMiddleware}
1367
1514
  registerRoutes(app);
1368
1515
 
1369
1516
  export type AppType = typeof app;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kozojs/cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "CLI to scaffold new Kozo Framework projects - The next-gen TypeScript Backend Framework",
5
5
  "bin": {
6
6
  "create-kozo": "./lib/index.js",