zuro-cli 0.0.2-beta.4 → 0.0.2-beta.5

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/dist/index.js CHANGED
@@ -202,18 +202,23 @@ async function ensurePackageManagerAvailable(pm) {
202
202
  );
203
203
  }
204
204
  }
205
- async function initPackageJson(cwd, force = false, packageName = "zuro-app", srcDir = "src") {
205
+ async function initPackageJson(cwd, force = false, packageName = "zuro-app", srcDir = "src", options = {}) {
206
206
  const pkgPath = import_path.default.join(cwd, "package.json");
207
207
  if (force || !await import_fs_extra.default.pathExists(pkgPath)) {
208
+ const scripts = {
209
+ "dev": `tsx watch ${srcDir}/server.ts`,
210
+ "build": "tsc",
211
+ "start": "node dist/server.js"
212
+ };
213
+ if (options.enablePrettier) {
214
+ scripts["format"] = "prettier --write .";
215
+ scripts["format:check"] = "prettier --check .";
216
+ }
208
217
  await import_fs_extra.default.writeJson(pkgPath, {
209
218
  name: normalizePackageName(packageName),
210
219
  version: "0.0.1",
211
220
  private: true,
212
- scripts: {
213
- "dev": `tsx watch ${srcDir}/server.ts`,
214
- "build": "tsc",
215
- "start": "node dist/server.js"
216
- }
221
+ scripts
217
222
  }, { spaces: 2 });
218
223
  }
219
224
  }
@@ -402,6 +407,13 @@ function showNonZuroProjectMessage() {
402
407
  console.log("- a fresh/empty directory, or");
403
408
  console.log("- an existing project already managed by Zuro CLI.");
404
409
  }
410
+ function showInitFirstMessage() {
411
+ console.log(import_chalk.default.yellow("No Zuro project found in this directory."));
412
+ console.log("");
413
+ console.log(import_chalk.default.yellow("Run init first, then add modules."));
414
+ console.log("");
415
+ console.log(import_chalk.default.cyan("npx zuro-cli init"));
416
+ }
405
417
 
406
418
  // src/commands/init.ts
407
419
  function resolveSafeTargetPath(projectRoot, srcDir, file) {
@@ -431,6 +443,33 @@ async function ensureSafeTargetDirectory(targetDir, cwd, projectName) {
431
443
  });
432
444
  return response.proceed === true;
433
445
  }
446
+ async function setupPrettier(targetDir) {
447
+ const prettierConfigPath = import_path4.default.join(targetDir, ".prettierrc");
448
+ const prettierIgnorePath = import_path4.default.join(targetDir, ".prettierignore");
449
+ if (!await import_fs_extra4.default.pathExists(prettierConfigPath)) {
450
+ const prettierConfig = {
451
+ semi: true,
452
+ singleQuote: false,
453
+ trailingComma: "es5",
454
+ printWidth: 100,
455
+ tabWidth: 2
456
+ };
457
+ await import_fs_extra4.default.writeJson(prettierConfigPath, prettierConfig, { spaces: 2 });
458
+ }
459
+ if (!await import_fs_extra4.default.pathExists(prettierIgnorePath)) {
460
+ const ignoreContent = `node_modules
461
+ dist
462
+ build
463
+ coverage
464
+ .next
465
+ pnpm-lock.yaml
466
+ package-lock.json
467
+ bun.lock
468
+ bun.lockb
469
+ `;
470
+ await import_fs_extra4.default.writeFile(prettierIgnorePath, ignoreContent);
471
+ }
472
+ }
434
473
  async function init() {
435
474
  const cwd = process.cwd();
436
475
  const isExistingProject = await import_fs_extra4.default.pathExists(import_path4.default.join(cwd, "package.json"));
@@ -443,6 +482,7 @@ async function init() {
443
482
  let pm = "npm";
444
483
  let srcDir = "src";
445
484
  let projectName = import_path4.default.basename(cwd);
485
+ let enablePrettier = false;
446
486
  if (isExistingProject) {
447
487
  console.log(import_chalk2.default.blue("\u2139 Existing project detected."));
448
488
  projectName = import_path4.default.basename(cwd);
@@ -480,6 +520,12 @@ async function init() {
480
520
  { title: "bun", value: "bun" }
481
521
  ],
482
522
  initial: 0
523
+ },
524
+ {
525
+ type: "confirm",
526
+ name: "prettier",
527
+ message: "Setup Prettier?",
528
+ initial: true
483
529
  }
484
530
  ]);
485
531
  if (response.pm === void 0) {
@@ -487,6 +533,7 @@ async function init() {
487
533
  return;
488
534
  }
489
535
  pm = response.pm;
536
+ enablePrettier = response.prettier === true;
490
537
  srcDir = "src";
491
538
  if (!response.path || response.path.trim() === "") {
492
539
  projectName = import_path4.default.basename(cwd);
@@ -525,7 +572,7 @@ async function init() {
525
572
  spinner.text = "Initializing project...";
526
573
  const hasPackageJson = await import_fs_extra4.default.pathExists(import_path4.default.join(targetDir, "package.json"));
527
574
  if (!hasPackageJson) {
528
- await initPackageJson(targetDir, true, projectName, srcDir);
575
+ await initPackageJson(targetDir, true, projectName, srcDir, { enablePrettier });
529
576
  }
530
577
  currentStep = "dependency installation";
531
578
  spinner.text = `Installing dependencies using ${pm}...`;
@@ -542,6 +589,9 @@ async function init() {
542
589
  }
543
590
  await installDependencies(pm, runtimeDeps, targetDir);
544
591
  await installDependencies(pm, devDeps, targetDir, { dev: true });
592
+ if (enablePrettier) {
593
+ await installDependencies(pm, ["prettier"], targetDir, { dev: true });
594
+ }
545
595
  currentStep = "module file generation";
546
596
  spinner.text = "Fetching core module files...";
547
597
  for (const file of coreModule.files) {
@@ -567,6 +617,10 @@ async function init() {
567
617
  }
568
618
  currentStep = "environment file setup";
569
619
  await createInitialEnv(targetDir);
620
+ if (enablePrettier) {
621
+ currentStep = "prettier setup";
622
+ await setupPrettier(targetDir);
623
+ }
570
624
  currentStep = "config write";
571
625
  await writeZuroConfig(targetDir, zuroConfig);
572
626
  spinner.succeed(import_chalk2.default.green("Project initialized successfully!"));
@@ -751,6 +805,12 @@ function getDatabaseSetupHint(moduleName, dbUrl) {
751
805
  return moduleName === "database-pg" ? "createdb <database_name>" : `mysql -e "CREATE DATABASE IF NOT EXISTS <database_name>;"`;
752
806
  }
753
807
  }
808
+ function getModuleDocsPath(moduleName) {
809
+ if (isDatabaseModule(moduleName)) {
810
+ return "database";
811
+ }
812
+ return moduleName;
813
+ }
754
814
  function escapeRegex(value) {
755
815
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
756
816
  }
@@ -763,6 +823,11 @@ async function hasEnvVariable(projectRoot, key) {
763
823
  const pattern = new RegExp(`^${escapeRegex(key)}=`, "m");
764
824
  return pattern.test(content);
765
825
  }
826
+ async function isLikelyEmptyDirectory(cwd) {
827
+ const entries = await import_fs_extra6.default.readdir(cwd);
828
+ const ignored = /* @__PURE__ */ new Set([".ds_store", "thumbs.db"]);
829
+ return entries.filter((entry) => !ignored.has(entry.toLowerCase())).length === 0;
830
+ }
766
831
  async function injectErrorHandler(projectRoot, srcDir) {
767
832
  const appPath = import_path6.default.join(projectRoot, srcDir, "app.ts");
768
833
  if (!import_fs_extra6.default.existsSync(appPath)) {
@@ -878,6 +943,10 @@ var add = async (moduleName) => {
878
943
  const projectRoot = process.cwd();
879
944
  const projectConfig = await readZuroConfig(projectRoot);
880
945
  if (!projectConfig) {
946
+ if (await isLikelyEmptyDirectory(projectRoot)) {
947
+ showInitFirstMessage();
948
+ return;
949
+ }
881
950
  showNonZuroProjectMessage();
882
951
  return;
883
952
  }
@@ -1052,71 +1121,27 @@ var add = async (moduleName) => {
1052
1121
  console.log(import_chalk4.default.blue(`\u2139 Backup created at: ${databaseBackupPath}
1053
1122
  `));
1054
1123
  }
1055
- if (resolvedModuleName === "auth") {
1056
- console.log(import_chalk4.default.bold("\u{1F4CB} Next Steps:\n"));
1057
- if (generatedAuthSecret) {
1058
- console.log(import_chalk4.default.yellow("1. BETTER_AUTH_SECRET generated automatically."));
1059
- } else {
1060
- console.log(import_chalk4.default.yellow("1. Review your auth env values in .env."));
1061
- }
1062
- console.log(import_chalk4.default.dim(" Make sure BETTER_AUTH_URL matches your API origin (for example http://localhost:3000).\n"));
1063
- console.log(import_chalk4.default.yellow("2. Run database migrations:"));
1064
- console.log(import_chalk4.default.cyan(" npx drizzle-kit generate"));
1065
- console.log(import_chalk4.default.cyan(" npx drizzle-kit migrate\n"));
1066
- console.log(import_chalk4.default.yellow("3. Available endpoints:"));
1067
- console.log(import_chalk4.default.dim(" POST /auth/sign-up/email - Register"));
1068
- console.log(import_chalk4.default.dim(" POST /auth/sign-in/email - Login"));
1069
- console.log(import_chalk4.default.dim(" POST /auth/sign-out - Logout"));
1070
- console.log(import_chalk4.default.dim(" GET /api/users/me - Current user\n"));
1071
- } else if (resolvedModuleName === "error-handler") {
1072
- console.log(import_chalk4.default.bold("\u{1F4CB} Usage:\n"));
1073
- console.log(import_chalk4.default.yellow("Throw errors in your controllers:"));
1074
- console.log(import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1075
- console.log(import_chalk4.default.white(` import { UnauthorizedError, NotFoundError } from "./lib/errors";`));
1076
- console.log("");
1077
- console.log(import_chalk4.default.white(` throw new UnauthorizedError("Invalid credentials");`));
1078
- console.log(import_chalk4.default.white(` throw new NotFoundError("User not found");`));
1079
- console.log(import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
1080
- console.log(import_chalk4.default.yellow("Available error classes:"));
1081
- console.log(import_chalk4.default.dim(" BadRequestError (400)"));
1082
- console.log(import_chalk4.default.dim(" UnauthorizedError (401)"));
1083
- console.log(import_chalk4.default.dim(" ForbiddenError (403)"));
1084
- console.log(import_chalk4.default.dim(" NotFoundError (404)"));
1085
- console.log(import_chalk4.default.dim(" ConflictError (409)"));
1086
- console.log(import_chalk4.default.dim(" ValidationError (422)"));
1087
- console.log(import_chalk4.default.dim(" InternalServerError (500)\n"));
1088
- console.log(import_chalk4.default.yellow("Wrap async handlers:"));
1089
- console.log(import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1090
- console.log(import_chalk4.default.white(` import { asyncHandler } from "./middleware/error-handler";`));
1091
- console.log("");
1092
- console.log(import_chalk4.default.white(` router.get("/users", asyncHandler(async (req, res) => {`));
1093
- console.log(import_chalk4.default.white(" // errors auto-caught"));
1094
- console.log(import_chalk4.default.white(" }));"));
1095
- console.log(import_chalk4.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
1096
- } else if (isDatabaseModule(resolvedModuleName)) {
1097
- console.log(import_chalk4.default.bold("\u{1F4CB} Next Steps:\n"));
1098
- let stepNum = 1;
1124
+ const docsPath = getModuleDocsPath(resolvedModuleName);
1125
+ const docsUrl = `https://zuro-cli.devbybriyan.com/docs/${docsPath}`;
1126
+ console.log(import_chalk4.default.blue(`\u2139 Docs: ${docsUrl}`));
1127
+ if (isDatabaseModule(resolvedModuleName)) {
1099
1128
  if (usedDefaultDbUrl) {
1100
- console.log(import_chalk4.default.yellow(`${stepNum}. Update DATABASE_URL in .env:`));
1101
- console.log(
1102
- import_chalk4.default.dim(" We added a local default. Update it if your DB host/user/password differ.\n")
1103
- );
1104
- stepNum++;
1129
+ console.log(import_chalk4.default.yellow("\u2139 Review DATABASE_URL in .env if your local DB config differs."));
1105
1130
  }
1106
- console.log(import_chalk4.default.yellow(`${stepNum}. Create schemas in ${srcDir}/db/schema/:`));
1107
- console.log(import_chalk4.default.dim(" Add table files and export from index.ts\n"));
1108
- stepNum++;
1109
1131
  const setupHint = getDatabaseSetupHint(
1110
1132
  resolvedModuleName,
1111
1133
  customDbUrl || DEFAULT_DATABASE_URLS[resolvedModuleName]
1112
1134
  );
1113
- console.log(import_chalk4.default.yellow(`${stepNum}. Ensure the database exists:`));
1114
- console.log(import_chalk4.default.cyan(` ${setupHint}
1115
- `));
1116
- stepNum++;
1117
- console.log(import_chalk4.default.yellow(`${stepNum}. Run migrations:`));
1118
- console.log(import_chalk4.default.cyan(" npx drizzle-kit generate"));
1119
- console.log(import_chalk4.default.cyan(" npx drizzle-kit migrate\n"));
1135
+ console.log(import_chalk4.default.yellow(`\u2139 Ensure DB exists: ${setupHint}`));
1136
+ console.log(import_chalk4.default.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
1137
+ }
1138
+ if (resolvedModuleName === "auth") {
1139
+ if (generatedAuthSecret) {
1140
+ console.log(import_chalk4.default.yellow("\u2139 BETTER_AUTH_SECRET was generated automatically."));
1141
+ } else {
1142
+ console.log(import_chalk4.default.yellow("\u2139 Review BETTER_AUTH_SECRET and BETTER_AUTH_URL in .env."));
1143
+ }
1144
+ console.log(import_chalk4.default.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
1120
1145
  }
1121
1146
  } catch (error) {
1122
1147
  spinner.fail(import_chalk4.default.red(`Failed during ${currentStep}.`));