create-zerra-app 1.0.2 → 1.2.0

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/index.js CHANGED
@@ -12,7 +12,7 @@ program
12
12
  .action(async (projectName) => {
13
13
  const targetPath = path.join(process.cwd(), projectName);
14
14
 
15
- // 1. Ask for Database Preference
15
+ // 1. Ask for Database Preference and CLI features
16
16
  const answers = await inquirer.prompt([
17
17
  {
18
18
  type: "list",
@@ -26,25 +26,169 @@ program
26
26
  { name: "Firebase", value: "js-firebase" },
27
27
  ],
28
28
  },
29
+ {
30
+ type: "confirm",
31
+ name: "includeAuth",
32
+ message: "Include Authentication Starter (JWT + Bcrypt)?",
33
+ default: true,
34
+ },
35
+ {
36
+ type: "checkbox",
37
+ name: "features",
38
+ message: "Select advanced features to enable:",
39
+ choices: [
40
+ { name: "Beautiful Request Logging", value: "logging", checked: true },
41
+ { name: "Dynamic Routing ([id].js)", value: "dynamicRouting", checked: true },
42
+ { name: "File-Based Middleware (_middleware.js)", value: "middleware", checked: true },
43
+ { name: "Auto-load Environment Variables (.env)", value: "dotenv", checked: true },
44
+ { name: "Automatic Input Validation (Schema)", value: "validation", checked: true },
45
+ { name: "Multipart File Uploads (req.files)", value: "multipart", checked: true },
46
+ { name: "Smart Error Handling (_error.js)", value: "errors", checked: true },
47
+ { name: "Dev Dashboard (/__zerra)", value: "dashboard", checked: true }
48
+ ]
49
+ },
50
+ {
51
+ type: "confirm",
52
+ name: "installDeps",
53
+ message: "Install dependencies automatically?",
54
+ default: true,
55
+ },
56
+ {
57
+ type: "confirm",
58
+ name: "initGit",
59
+ message: "Initialize git repository?",
60
+ default: true,
61
+ }
29
62
  ]);
30
63
 
31
- const templatePath = path.join(__dirname, "templates", answers.database);
64
+ // 2. Project Preview Summary
65
+ console.log(`\nšŸ“‹ Project Configuration:`);
66
+ console.log(` - Project Name: \x1b[36m${projectName}\x1b[0m`);
67
+ console.log(` - Database: \x1b[33m${answers.database.replace('js-', '')}\x1b[0m`);
68
+ console.log(` - Auth Starter: \x1b[32m${answers.includeAuth ? 'Enabled' : 'Disabled'}\x1b[0m`);
69
+ console.log(` - Features: \x1b[35m${answers.features.join(', ') || 'None'}\x1b[0m`);
70
+ console.log(` - Auto-Install: \x1b[32m${answers.installDeps ? 'Yes' : 'No'}\x1b[0m`);
71
+ console.log(` - Git Init: \x1b[32m${answers.initGit ? 'Yes' : 'No'}\x1b[0m`);
32
72
 
33
- console.log(`\nšŸ—ļø Generating Zerra project: ${projectName}...`);
73
+ const { confirmProceed } = await inquirer.prompt([
74
+ {
75
+ type: "confirm",
76
+ name: "confirmProceed",
77
+ message: "Does this look correct?",
78
+ default: true,
79
+ }
80
+ ]);
81
+
82
+ if (!confirmProceed) {
83
+ console.log("\nāŒ Project creation cancelled.\n");
84
+ return;
85
+ }
86
+
87
+ const baseTemplatePath = path.join(__dirname, "templates", "js-base");
88
+ const dbTemplatePath = path.join(__dirname, "templates", answers.database);
89
+ const authTemplatePath = path.join(__dirname, "templates", "js-auth");
90
+
91
+ console.log(`\nšŸ—ļø Building your Zerra application...`);
34
92
 
35
93
  try {
36
- if (!fs.existsSync(templatePath)) {
37
- // Fallback to base if specific template isn't ready yet
38
- console.warn(`āš ļø Template ${answers.database} not found, falling back to base.`);
39
- await fs.copy(path.join(__dirname, "templates", "js-base"), targetPath);
40
- } else {
41
- await fs.copy(templatePath, targetPath);
94
+ // 1. Copy the Base Template (Foundation)
95
+ if (fs.existsSync(baseTemplatePath)) {
96
+ await fs.copy(baseTemplatePath, targetPath);
97
+ }
98
+
99
+ // 2. Overlay Database-Specific Template (if not base)
100
+ if (answers.database !== "js-base" && fs.existsSync(dbTemplatePath)) {
101
+ // We use a custom copy to handle package.json merging
102
+ await fs.copy(dbTemplatePath, targetPath, {
103
+ overwrite: true,
104
+ filter: (src) => !src.endsWith("package.json"), // Handle package.json separately
105
+ });
106
+
107
+ const dbPkgPath = path.join(dbTemplatePath, "package.json");
108
+ const targetPkgPath = path.join(targetPath, "package.json");
109
+
110
+ if (fs.existsSync(dbPkgPath) && fs.existsSync(targetPkgPath)) {
111
+ const basePkg = await fs.readJson(targetPkgPath);
112
+ const dbPkg = await fs.readJson(dbPkgPath);
113
+
114
+ // Merge dependencies and scripts
115
+ basePkg.dependencies = { ...(basePkg.dependencies || {}), ...(dbPkg.dependencies || {}) };
116
+ basePkg.scripts = { ...(basePkg.scripts || {}), ...(dbPkg.scripts || {}) };
117
+
118
+ await fs.writeJson(targetPkgPath, basePkg, { spaces: 2 });
119
+ }
120
+ }
121
+
122
+ // 2.5 Overlay Auth Starter (if selected)
123
+ if (answers.includeAuth && fs.existsSync(authTemplatePath)) {
124
+ console.log(` šŸ” Adding Auth Starter...`);
125
+ await fs.copy(authTemplatePath, targetPath, {
126
+ overwrite: true,
127
+ filter: (src) => !src.endsWith("package.json"),
128
+ });
129
+
130
+ const authPkgPath = path.join(authTemplatePath, "package.json");
131
+ const targetPkgPath = path.join(targetPath, "package.json");
132
+
133
+ if (fs.existsSync(authPkgPath) && fs.existsSync(targetPkgPath)) {
134
+ const basePkg = await fs.readJson(targetPkgPath);
135
+ const authPkg = await fs.readJson(authPkgPath);
136
+ basePkg.dependencies = { ...(basePkg.dependencies || {}), ...(authPkg.dependencies || {}) };
137
+ await fs.writeJson(targetPkgPath, basePkg, { spaces: 2 });
138
+ }
42
139
  }
43
140
 
44
- console.log(`šŸš€ Zerra project created at ${targetPath}`);
141
+ // 3. Customize project name in package.json
142
+ const pkgPath = path.join(targetPath, "package.json");
143
+ if (fs.existsSync(pkgPath)) {
144
+ const pkg = await fs.readJson(pkgPath);
145
+ pkg.name = projectName;
146
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
147
+ }
148
+
149
+ // 4. Generate zerra.config.json based on feature selection
150
+ const featureConfig = {
151
+ logging: answers.features.includes('logging'),
152
+ dynamicRouting: answers.features.includes('dynamicRouting'),
153
+ middleware: answers.features.includes('middleware'),
154
+ dotenv: answers.features.includes('dotenv'),
155
+ validation: answers.features.includes('validation'),
156
+ multipart: answers.features.includes('multipart'),
157
+ errors: answers.features.includes('errors'),
158
+ dashboard: answers.features.includes('dashboard')
159
+ };
160
+
161
+ const configJsonPath = path.join(targetPath, 'zerra.config.json');
162
+ await fs.writeJson(configJsonPath, { features: featureConfig, plugins: [] }, { spaces: 2 });
163
+
164
+ const { execSync } = require("child_process");
165
+
166
+ if (answers.installDeps) {
167
+ console.log(`\nšŸ“¦ Installing dependencies...`);
168
+ try {
169
+ execSync("npm install", { cwd: targetPath, stdio: "inherit" });
170
+ } catch (installErr) {
171
+ console.warn(`āš ļø Failed to install dependencies automatically. You may need to run 'npm install' manually.`);
172
+ }
173
+ }
174
+
175
+ if (answers.initGit) {
176
+ try {
177
+ execSync("git init", { cwd: targetPath, stdio: "ignore" });
178
+ execSync("git add .", { cwd: targetPath, stdio: "ignore" });
179
+ execSync('git commit -m "Initial commit from create-zerra-app"', { cwd: targetPath, stdio: "ignore" });
180
+ console.log(`🌱 Initialized a git repository.`);
181
+ } catch (gitErr) {
182
+ // Ignore git errors
183
+ }
184
+ }
185
+
186
+ console.log(`\nšŸš€ Zerra project created successfully at ${targetPath}`);
45
187
  console.log(`\nNext steps:`);
46
188
  console.log(` cd ${projectName}`);
47
- console.log(` npm install`);
189
+ if (!answers.installDeps) {
190
+ console.log(` npm install`);
191
+ }
48
192
  console.log(` npm run dev\n`);
49
193
  } catch (err) {
50
194
  console.error("āŒ Error creating project:", err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zerra-app",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,15 @@
1
+ const auth = require('../../services/auth');
2
+
3
+ module.exports = async (req, res) => {
4
+ const { email, password } = req.body;
5
+
6
+ // Mock user for the starter
7
+ const mockUser = { id: 1, email: 'user@example.com', password: auth.hashPassword('password123') };
8
+
9
+ if (email === mockUser.email && auth.comparePassword(password, mockUser.password)) {
10
+ const token = auth.generateToken(mockUser);
11
+ return res.json({ token, message: "Login successful" });
12
+ }
13
+
14
+ res.status(401).json({ error: "Invalid credentials" });
15
+ };
@@ -0,0 +1,24 @@
1
+ const auth = require('../../services/auth');
2
+
3
+ // Note: This is a starter. In a real app, you would save the user to your database.
4
+ module.exports = async (req, res) => {
5
+ const { email, password } = req.body;
6
+
7
+ if (!email || !password) {
8
+ return res.status(400).json({ error: "Email and password are required" });
9
+ }
10
+
11
+ const hashedPassword = auth.hashPassword(password);
12
+
13
+ // Here you would: await db.users.create({ email, password: hashedPassword })
14
+
15
+ res.status(201).json({
16
+ message: "User registered successfully (Starter Mock)",
17
+ user: { email }
18
+ });
19
+ };
20
+
21
+ module.exports.schema = {
22
+ email: 'string',
23
+ password: 'string'
24
+ };
@@ -0,0 +1,17 @@
1
+ const auth = require('../../services/auth');
2
+
3
+ module.exports = async (req, res, next) => {
4
+ const token = req.headers.authorization?.split(' ')[1];
5
+
6
+ if (!token) {
7
+ return res.status(401).json({ error: "No token provided" });
8
+ }
9
+
10
+ try {
11
+ const decoded = auth.verifyToken(token);
12
+ req.user = decoded;
13
+ await next();
14
+ } catch (e) {
15
+ res.status(401).json({ error: "Invalid or expired token" });
16
+ }
17
+ };
@@ -0,0 +1,6 @@
1
+ module.exports = async (req, res) => {
2
+ res.json({
3
+ message: "Welcome to the secret area!",
4
+ user: req.user
5
+ });
6
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "dependencies": {
3
+ "jsonwebtoken": "^9.0.2",
4
+ "bcryptjs": "^2.4.3"
5
+ }
6
+ }
@@ -0,0 +1,11 @@
1
+ const jwt = require('jsonwebtoken');
2
+ const bcrypt = require('bcryptjs');
3
+
4
+ const JWT_SECRET = process.env.JWT_SECRET || 'zerra-secret-key';
5
+
6
+ module.exports = {
7
+ hashPassword: (password) => bcrypt.hashSync(password, 10),
8
+ comparePassword: (password, hash) => bcrypt.compareSync(password, hash),
9
+ generateToken: (user) => jwt.sign({ id: user.id, email: user.email }, JWT_SECRET, { expiresIn: '1d' }),
10
+ verifyToken: (token) => jwt.verify(token, JWT_SECRET)
11
+ };
@@ -1,3 +1,10 @@
1
+ const logger = require("../services/logger");
2
+
1
3
  module.exports = (req, res) => {
2
- res.end("Hello from Zerra! šŸš€");
4
+ logger.info("Hello endpoint was hit!");
5
+ res.json({
6
+ message: "Hello from Zerra! šŸš€",
7
+ timestamp: new Date().toISOString(),
8
+ query: req.query
9
+ });
3
10
  };
@@ -5,6 +5,6 @@
5
5
  "dev": "node server.js"
6
6
  },
7
7
  "dependencies": {
8
- "zerra-core": "^1.0.0"
8
+ "zerra-core": "^1.2.0"
9
9
  }
10
10
  }
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ info: (message) => console.log(`[INFO] ${new Date().toISOString()}: ${message}`),
3
+ error: (message) => console.error(`[ERROR] ${new Date().toISOString()}: ${message}`),
4
+ };
@@ -1,3 +1,9 @@
1
- module.exports = (req, res) => {
2
- res.end("Hello from Zerra! šŸš€");
1
+ const firebase = require("../services/firebase");
2
+
3
+ module.exports = async (req, res) => {
4
+ const snapshot = await firebase.firestore().collection("users").get();
5
+ res.json({
6
+ message: "Hello from Zerra! Firebase is ready.",
7
+ userCount: snapshot.docs.length
8
+ });
3
9
  };
@@ -1,10 +1,5 @@
1
1
  {
2
- "name": "zerra-app",
3
- "version": "1.0.0",
4
- "scripts": {
5
- "dev": "node server.js"
6
- },
7
2
  "dependencies": {
8
- "zerra-core": "^1.0.0"
3
+ "firebase-admin": "^11.0.0"
9
4
  }
10
5
  }
@@ -0,0 +1,11 @@
1
+ // This is a stub for your Firebase Admin SDK
2
+ module.exports = {
3
+ firestore: () => ({
4
+ collection: (name) => ({
5
+ get: async () => {
6
+ console.log(`[FIREBASE] Getting collection: ${name}`);
7
+ return { docs: [] };
8
+ }
9
+ })
10
+ })
11
+ };
@@ -1,3 +1,10 @@
1
- module.exports = (req, res) => {
2
- res.end("Hello from Zerra! šŸš€");
1
+ const db = require("../services/db");
2
+
3
+ module.exports = async (req, res) => {
4
+ const users = await db.find("users", {});
5
+ res.json({
6
+ message: "Hello from Zerra! MongoDB is connected.",
7
+ count: users.length,
8
+ users: users
9
+ });
3
10
  };
@@ -1,10 +1,5 @@
1
1
  {
2
- "name": "zerra-app",
3
- "version": "1.0.0",
4
- "scripts": {
5
- "dev": "node server.js"
6
- },
7
2
  "dependencies": {
8
- "zerra-core": "^1.0.0"
3
+ "mongodb": "^6.0.0"
9
4
  }
10
5
  }
@@ -0,0 +1,7 @@
1
+ // This is a stub for your MongoDB connection (e.g., using Mongoose or mongodb driver)
2
+ module.exports = {
3
+ find: async (collection, query) => {
4
+ console.log(`[MONGO] Finding in ${collection}:`, query);
5
+ return []; // Return mock results
6
+ }
7
+ };
@@ -1,3 +1,6 @@
1
- module.exports = (req, res) => {
2
- res.end("Hello from Zerra! šŸš€");
1
+ const db = require("../services/db");
2
+
3
+ module.exports = async (req, res) => {
4
+ const users = await db.query("SELECT * FROM users");
5
+ res.end(`Hello from Zerra! Database is connected. Found ${users.length} users.`);
3
6
  };
@@ -1,10 +1,6 @@
1
1
  {
2
- "name": "zerra-app",
3
- "version": "1.0.0",
4
- "scripts": {
5
- "dev": "node server.js"
6
- },
7
2
  "dependencies": {
8
- "zerra-core": "^1.0.0"
3
+ "pg": "^8.0.0",
4
+ "mysql2": "^3.0.0"
9
5
  }
10
6
  }
@@ -0,0 +1,7 @@
1
+ // This is a stub for your SQL database connection (e.g., using Knex, Sequelize, or pg)
2
+ module.exports = {
3
+ query: async (sql, params) => {
4
+ console.log(`[DB QUERY] Executing: ${sql} with params:`, params);
5
+ return []; // Return mock results
6
+ }
7
+ };
@@ -1,3 +1,10 @@
1
- module.exports = (req, res) => {
2
- res.end("Hello from Zerra! šŸš€");
1
+ const supabase = require("../services/supabase");
2
+
3
+ module.exports = async (req, res) => {
4
+ const { data, error } = await supabase.from("profiles").select("*");
5
+ res.json({
6
+ message: "Hello from Zerra! Supabase is ready.",
7
+ profiles: data,
8
+ error: error
9
+ });
3
10
  };
@@ -1,10 +1,5 @@
1
1
  {
2
- "name": "zerra-app",
3
- "version": "1.0.0",
4
- "scripts": {
5
- "dev": "node server.js"
6
- },
7
2
  "dependencies": {
8
- "zerra-core": "^1.0.0"
3
+ "@supabase/supabase-js": "^2.0.0"
9
4
  }
10
5
  }
@@ -0,0 +1,9 @@
1
+ // This is a stub for your Supabase client
2
+ module.exports = {
3
+ from: (table) => ({
4
+ select: () => {
5
+ console.log(`[SUPABASE] Selecting from ${table}`);
6
+ return { data: [], error: null };
7
+ }
8
+ })
9
+ };
@@ -1 +0,0 @@
1
- console.log("Welcome to your new Zerra application!");
@@ -1,3 +0,0 @@
1
- const { startServer } = require("zerra-core");
2
-
3
- startServer(3000);
@@ -1,3 +0,0 @@
1
- const { startServer } = require("zerra-core");
2
-
3
- startServer(3000);
@@ -1,3 +0,0 @@
1
- const { startServer } = require("zerra-core");
2
-
3
- startServer(3000);
@@ -1,3 +0,0 @@
1
- const { startServer } = require("zerra-core");
2
-
3
- startServer(3000);