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

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,34 @@ 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
+ }
831
+ async function ensureSchemaExport(projectRoot, srcDir, schemaFileName) {
832
+ const schemaIndexPath = import_path6.default.join(projectRoot, srcDir, "db", "schema", "index.ts");
833
+ if (!await import_fs_extra6.default.pathExists(schemaIndexPath)) {
834
+ return;
835
+ }
836
+ const exportLine = `export * from "./${schemaFileName}";`;
837
+ const content = await import_fs_extra6.default.readFile(schemaIndexPath, "utf-8");
838
+ const normalized = content.replace(/\r\n/g, "\n");
839
+ const exportPattern = new RegExp(
840
+ `^\\s*export\\s*\\*\\s*from\\s*["']\\./${escapeRegex(schemaFileName)}["'];?\\s*$`,
841
+ "m"
842
+ );
843
+ if (exportPattern.test(normalized)) {
844
+ return;
845
+ }
846
+ let next = normalized.replace(/^\s*export\s*\{\s*\};?\s*$/m, "").trimEnd();
847
+ if (next.length > 0) {
848
+ next += "\n\n";
849
+ }
850
+ next += `${exportLine}
851
+ `;
852
+ await import_fs_extra6.default.writeFile(schemaIndexPath, next);
853
+ }
766
854
  async function injectErrorHandler(projectRoot, srcDir) {
767
855
  const appPath = import_path6.default.join(projectRoot, srcDir, "app.ts");
768
856
  if (!import_fs_extra6.default.existsSync(appPath)) {
@@ -878,6 +966,10 @@ var add = async (moduleName) => {
878
966
  const projectRoot = process.cwd();
879
967
  const projectConfig = await readZuroConfig(projectRoot);
880
968
  if (!projectConfig) {
969
+ if (await isLikelyEmptyDirectory(projectRoot)) {
970
+ showInitFirstMessage();
971
+ return;
972
+ }
881
973
  showNonZuroProjectMessage();
882
974
  return;
883
975
  }
@@ -1005,6 +1097,10 @@ var add = async (moduleName) => {
1005
1097
  await import_fs_extra6.default.ensureDir(import_path6.default.dirname(targetPath));
1006
1098
  await import_fs_extra6.default.writeFile(targetPath, content);
1007
1099
  }
1100
+ const schemaExports = module2.files.map((file) => file.target.replace(/\\/g, "/")).filter((target) => /^db\/schema\/[^/]+\.ts$/.test(target)).map((target) => import_path6.default.posix.basename(target, ".ts")).filter((name) => name !== "index");
1101
+ for (const schemaFileName of schemaExports) {
1102
+ await ensureSchemaExport(projectRoot, srcDir, schemaFileName);
1103
+ }
1008
1104
  spinner.succeed("Files generated");
1009
1105
  if (resolvedModuleName === "auth") {
1010
1106
  spinner.start("Configuring routes in app.ts...");
@@ -1052,71 +1148,27 @@ var add = async (moduleName) => {
1052
1148
  console.log(import_chalk4.default.blue(`\u2139 Backup created at: ${databaseBackupPath}
1053
1149
  `));
1054
1150
  }
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;
1151
+ const docsPath = getModuleDocsPath(resolvedModuleName);
1152
+ const docsUrl = `https://zuro-cli.devbybriyan.com/docs/${docsPath}`;
1153
+ console.log(import_chalk4.default.blue(`\u2139 Docs: ${docsUrl}`));
1154
+ if (isDatabaseModule(resolvedModuleName)) {
1099
1155
  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++;
1156
+ console.log(import_chalk4.default.yellow("\u2139 Review DATABASE_URL in .env if your local DB config differs."));
1105
1157
  }
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
1158
  const setupHint = getDatabaseSetupHint(
1110
1159
  resolvedModuleName,
1111
1160
  customDbUrl || DEFAULT_DATABASE_URLS[resolvedModuleName]
1112
1161
  );
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"));
1162
+ console.log(import_chalk4.default.yellow(`\u2139 Ensure DB exists: ${setupHint}`));
1163
+ console.log(import_chalk4.default.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
1164
+ }
1165
+ if (resolvedModuleName === "auth") {
1166
+ if (generatedAuthSecret) {
1167
+ console.log(import_chalk4.default.yellow("\u2139 BETTER_AUTH_SECRET was generated automatically."));
1168
+ } else {
1169
+ console.log(import_chalk4.default.yellow("\u2139 Review BETTER_AUTH_SECRET and BETTER_AUTH_URL in .env."));
1170
+ }
1171
+ console.log(import_chalk4.default.yellow("\u2139 Run migrations: npx drizzle-kit generate && npx drizzle-kit migrate"));
1120
1172
  }
1121
1173
  } catch (error) {
1122
1174
  spinner.fail(import_chalk4.default.red(`Failed during ${currentStep}.`));