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 +118 -66
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +118 -66
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
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(
|
|
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(
|
|
1114
|
-
console.log(import_chalk4.default.
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
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}.`));
|