create-zerra-app 1.1.0 → 1.2.1

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
@@ -27,36 +27,79 @@ program
27
27
  ],
28
28
  },
29
29
  {
30
- type: "confirm",
31
- name: "installDeps",
32
- message: "Would you like to install dependencies automatically?",
33
- default: true,
30
+ type: "list",
31
+ name: "language",
32
+ message: "Choose your primary language:",
33
+ choices: [
34
+ { name: "JavaScript", value: "js" },
35
+ { name: "TypeScript", value: "ts" },
36
+ ],
37
+ default: "js",
34
38
  },
35
39
  {
36
40
  type: "confirm",
37
- name: "initGit",
38
- message: "Would you like to initialize a new git repository?",
41
+ name: "includeAuth",
42
+ message: "Include Authentication Starter (JWT + Bcrypt)?",
39
43
  default: true,
40
44
  },
41
45
  {
42
46
  type: "checkbox",
43
47
  name: "features",
44
- message: "Which advanced Zerra features would you like to enable?",
48
+ message: "Select advanced features to enable:",
45
49
  choices: [
46
50
  { name: "Beautiful Request Logging", value: "logging", checked: true },
47
51
  { name: "Dynamic Routing ([id].js)", value: "dynamicRouting", checked: true },
48
52
  { name: "File-Based Middleware (_middleware.js)", value: "middleware", checked: true },
49
53
  { name: "Auto-load Environment Variables (.env)", value: "dotenv", checked: true },
50
54
  { name: "Automatic Input Validation (Schema)", value: "validation", checked: true },
51
- { name: "Multipart File Uploads (req.files)", value: "multipart", checked: true }
55
+ { name: "Multipart File Uploads (req.files)", value: "multipart", checked: true },
56
+ { name: "Smart Error Handling (_error.js)", value: "errors", checked: true },
57
+ { name: "Dev Dashboard (/__zerra)", value: "dashboard", checked: true }
52
58
  ]
59
+ },
60
+ {
61
+ type: "confirm",
62
+ name: "installDeps",
63
+ message: "Install dependencies automatically?",
64
+ default: true,
65
+ },
66
+ {
67
+ type: "confirm",
68
+ name: "initGit",
69
+ message: "Initialize git repository?",
70
+ default: true,
53
71
  }
54
72
  ]);
55
73
 
74
+ // 2. Project Preview Summary
75
+ console.log(`\nšŸ“‹ Project Configuration:`);
76
+ console.log(` - Project Name: \x1b[36m${projectName}\x1b[0m`);
77
+ console.log(` - Database: \x1b[33m${answers.database.replace('js-', '')}\x1b[0m`);
78
+ console.log(` - Auth Starter: \x1b[32m${answers.includeAuth ? 'Enabled' : 'Disabled'}\x1b[0m`);
79
+ console.log(` - Language: \x1b[36m${answers.language.toUpperCase()}\x1b[0m`);
80
+ console.log(` - Features: \x1b[35m${answers.features.join(', ') || 'None'}\x1b[0m`);
81
+ console.log(` - Auto-Install: \x1b[32m${answers.installDeps ? 'Yes' : 'No'}\x1b[0m`);
82
+ console.log(` - Git Init: \x1b[32m${answers.initGit ? 'Yes' : 'No'}\x1b[0m`);
83
+
84
+ const { confirmProceed } = await inquirer.prompt([
85
+ {
86
+ type: "confirm",
87
+ name: "confirmProceed",
88
+ message: "Does this look correct?",
89
+ default: true,
90
+ }
91
+ ]);
92
+
93
+ if (!confirmProceed) {
94
+ console.log("\nāŒ Project creation cancelled.\n");
95
+ return;
96
+ }
97
+
56
98
  const baseTemplatePath = path.join(__dirname, "templates", "js-base");
57
99
  const dbTemplatePath = path.join(__dirname, "templates", answers.database);
100
+ const authTemplatePath = path.join(__dirname, "templates", "js-auth");
58
101
 
59
- console.log(`\nšŸ—ļø Generating Zerra project: ${projectName}...`);
102
+ console.log(`\nšŸ—ļø Building your Zerra application...`);
60
103
 
61
104
  try {
62
105
  // 1. Copy the Base Template (Foundation)
@@ -87,14 +130,91 @@ program
87
130
  }
88
131
  }
89
132
 
133
+ // 2.5 Overlay Auth Starter (if selected)
134
+ if (answers.includeAuth && fs.existsSync(authTemplatePath)) {
135
+ console.log(` šŸ” Adding Auth Starter...`);
136
+ await fs.copy(authTemplatePath, targetPath, {
137
+ overwrite: true,
138
+ filter: (src) => !src.endsWith("package.json"),
139
+ });
140
+
141
+ const authPkgPath = path.join(authTemplatePath, "package.json");
142
+ const targetPkgPath = path.join(targetPath, "package.json");
143
+
144
+ if (fs.existsSync(authPkgPath) && fs.existsSync(targetPkgPath)) {
145
+ const basePkg = await fs.readJson(targetPkgPath);
146
+ const authPkg = await fs.readJson(authPkgPath);
147
+ basePkg.dependencies = { ...(basePkg.dependencies || {}), ...(authPkg.dependencies || {}) };
148
+ await fs.writeJson(targetPkgPath, basePkg, { spaces: 2 });
149
+ }
150
+ }
151
+
90
152
  // 3. Customize project name in package.json
91
153
  const pkgPath = path.join(targetPath, "package.json");
92
154
  if (fs.existsSync(pkgPath)) {
93
155
  const pkg = await fs.readJson(pkgPath);
94
156
  pkg.name = projectName;
157
+
158
+ if (answers.language === 'ts') {
159
+ pkg.devDependencies = {
160
+ ...(pkg.devDependencies || {}),
161
+ "typescript": "^5.0.0",
162
+ "@types/node": "^20.0.0",
163
+ "tsx": "^4.0.0"
164
+ };
165
+ pkg.scripts = {
166
+ ...(pkg.scripts || {}),
167
+ "dev": "tsx watch server.js",
168
+ "build": "tsc",
169
+ "start": "node server.js"
170
+ };
171
+ }
95
172
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
96
173
  }
97
174
 
175
+ // 3.5 Handle TypeScript File Renaming and tsconfig
176
+ if (answers.language === 'ts') {
177
+ console.log(` TypeScript-ifying your project...`);
178
+
179
+ // Generate tsconfig.json
180
+ const tsconfig = {
181
+ compilerOptions: {
182
+ target: "ESNext",
183
+ module: "CommonJS",
184
+ moduleResolution: "node",
185
+ esModuleInterop: true,
186
+ forceConsistentCasingInFileNames: true,
187
+ strict: true,
188
+ skipLibCheck: true,
189
+ outDir: "./dist"
190
+ },
191
+ include: ["api/**/*", "services/**/*", "server.js", "zerra.config.json"]
192
+ };
193
+ await fs.writeJson(path.join(targetPath, "tsconfig.json"), tsconfig, { spaces: 2 });
194
+
195
+ // Recursive function to rename .js to .ts
196
+ const renameJsToTs = async (dir) => {
197
+ const files = await fs.readdir(dir);
198
+ for (const file of files) {
199
+ const fullPath = path.join(dir, file);
200
+ const stat = await fs.stat(fullPath);
201
+ if (stat.isDirectory()) {
202
+ await renameJsToTs(fullPath);
203
+ } else if (file.endsWith(".js") && !file.startsWith("server.js")) {
204
+ const newPath = fullPath.replace(/\.js$/, ".ts");
205
+ await fs.move(fullPath, newPath);
206
+ }
207
+ }
208
+ };
209
+
210
+ if (fs.existsSync(path.join(targetPath, "api"))) {
211
+ await renameJsToTs(path.join(targetPath, "api"));
212
+ }
213
+ if (fs.existsSync(path.join(targetPath, "services"))) {
214
+ await renameJsToTs(path.join(targetPath, "services"));
215
+ }
216
+ }
217
+
98
218
  // 4. Generate zerra.config.json based on feature selection
99
219
  const featureConfig = {
100
220
  logging: answers.features.includes('logging'),
@@ -102,11 +222,26 @@ program
102
222
  middleware: answers.features.includes('middleware'),
103
223
  dotenv: answers.features.includes('dotenv'),
104
224
  validation: answers.features.includes('validation'),
105
- multipart: answers.features.includes('multipart')
225
+ multipart: answers.features.includes('multipart'),
226
+ errors: answers.features.includes('errors'),
227
+ dashboard: answers.features.includes('dashboard')
106
228
  };
107
229
 
108
230
  const configJsonPath = path.join(targetPath, 'zerra.config.json');
109
- await fs.writeJson(configJsonPath, { features: featureConfig }, { spaces: 2 });
231
+ await fs.writeJson(configJsonPath, { features: featureConfig, plugins: [] }, { spaces: 2 });
232
+
233
+ // 4.5 Generate .gitignore for the new project
234
+ const gitignoreContent = `node_modules/
235
+ dist/
236
+ build/
237
+ .env
238
+ .env.local
239
+ .env.*.local
240
+ *.log
241
+ .DS_Store
242
+ ${answers.language === 'ts' ? '*.tsbuildinfo' : ''}
243
+ `;
244
+ await fs.writeFile(path.join(targetPath, '.gitignore'), gitignoreContent);
110
245
 
111
246
  const { execSync } = require("child_process");
112
247
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-zerra-app",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
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
+ };
@@ -5,6 +5,6 @@
5
5
  "dev": "node server.js"
6
6
  },
7
7
  "dependencies": {
8
- "zerra-core": "^1.1.0"
8
+ "zerra-core": "^1.2.1"
9
9
  }
10
10
  }