projects-init-cli 2.0.0 → 2.1.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/dist/index.js CHANGED
@@ -12,7 +12,7 @@ const program = new commander_1.Command();
12
12
  program
13
13
  .name('projects-init')
14
14
  .description('CLI tool to initialize projects with customizable tech stacks')
15
- .version('1.0.0')
15
+ .version('2.1.0')
16
16
  .action(async () => {
17
17
  try {
18
18
  console.log(chalk_1.default.blue.bold('\nšŸš€ Project Initializer CLI\n'));
package/dist/prompts.js CHANGED
@@ -4,14 +4,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.promptUser = promptUser;
7
+ const path_1 = __importDefault(require("path"));
7
8
  const inquirer_1 = __importDefault(require("inquirer"));
9
+ function getDefaultProjectName() {
10
+ const folderName = path_1.default.basename(process.cwd());
11
+ const sanitized = folderName
12
+ .toLowerCase()
13
+ .replace(/[\s_]+/g, '-')
14
+ .replace(/[^a-z0-9-]/g, '')
15
+ .replace(/-+/g, '-')
16
+ .replace(/^-|-$/g, '');
17
+ return sanitized || 'my-project';
18
+ }
8
19
  async function promptUser() {
9
20
  const answers = await inquirer_1.default.prompt([
10
21
  {
11
22
  type: 'input',
12
23
  name: 'projectName',
13
24
  message: 'What is your project name?',
14
- default: 'my-project',
25
+ default: getDefaultProjectName(),
15
26
  validate: (input) => {
16
27
  if (!input.trim()) {
17
28
  return 'Project name cannot be empty';
@@ -51,6 +62,7 @@ async function promptUser() {
51
62
  choices: [
52
63
  { name: 'CDK to create database', value: 'cdk' },
53
64
  { name: 'Local SQLite (Node.js)', value: 'local-sqlite' },
65
+ { name: 'Local JSON file', value: 'local-json' },
54
66
  { name: 'External database URL', value: 'external-url' }
55
67
  ]
56
68
  }
@@ -78,8 +90,8 @@ async function promptUser() {
78
90
  ]);
79
91
  config.databaseUrl = urlAnswer.databaseUrl;
80
92
  }
81
- // Ask for database type if backend is not CDK/SAM or storage is not CDK
82
- if (config.backend !== 'cdk' && config.backend !== 'sam' && config.storage !== 'cdk') {
93
+ // Ask for database type if backend is not CDK/SAM or storage is not CDK/local-json
94
+ if (config.backend !== 'cdk' && config.backend !== 'sam' && config.storage !== 'cdk' && config.storage !== 'local-json') {
83
95
  const dbTypeAnswer = await inquirer_1.default.prompt([
84
96
  {
85
97
  type: 'list',
@@ -26,7 +26,10 @@ async function generateExpress(backendPath, config) {
26
26
  '@vitest/coverage-v8': '^2.1.3'
27
27
  };
28
28
  // Add database dependencies
29
- if (config.storage === 'local-sqlite') {
29
+ if (config.storage === 'local-json') {
30
+ // No additional dependencies needed for JSON file storage
31
+ }
32
+ else if (config.storage === 'local-sqlite') {
30
33
  if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
31
34
  dependencies['@prisma/client'] = '^6.0.1';
32
35
  devDependencies['prisma'] = '^6.0.1';
@@ -185,7 +188,10 @@ export default app;
185
188
  }
186
189
  await fs_extra_1.default.writeFile(path_1.default.join(backendPath, '.env.example'), envExample);
187
190
  // Create database setup if needed
188
- if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
191
+ if (config.storage === 'local-json') {
192
+ await generateJSONFileSetup(srcPath, config);
193
+ }
194
+ else if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
189
195
  await generatePrismaConfig(backendPath, config);
190
196
  }
191
197
  else if (config.databaseType === 'sql' && config.storage === 'local-sqlite') {
@@ -337,3 +343,85 @@ export default docClient;
337
343
  `;
338
344
  await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'db.ts'), dbContent);
339
345
  }
346
+ async function generateJSONFileSetup(srcPath, config) {
347
+ const backendPath = path_1.default.dirname(path_1.default.dirname(srcPath));
348
+ const dbContent = `import fs from 'fs-extra';
349
+ import path from 'path';
350
+
351
+ const DB_FILE = path.join(process.cwd(), 'data', 'database.json');
352
+
353
+ // Ensure data directory exists
354
+ const dataDir = path.dirname(DB_FILE);
355
+ fs.ensureDirSync(dataDir);
356
+
357
+ // Initialize database file if it doesn't exist
358
+ if (!fs.existsSync(DB_FILE)) {
359
+ fs.writeJSONSync(DB_FILE, { users: [] }, { spaces: 2 });
360
+ }
361
+
362
+ // Read database
363
+ export const readDB = (): any => {
364
+ try {
365
+ return fs.readJSONSync(DB_FILE);
366
+ } catch (error) {
367
+ console.error('Error reading database:', error);
368
+ return { users: [] };
369
+ }
370
+ };
371
+
372
+ // Write database
373
+ export const writeDB = (data: any): void => {
374
+ try {
375
+ fs.writeJSONSync(DB_FILE, data, { spaces: 2 });
376
+ } catch (error) {
377
+ console.error('Error writing database:', error);
378
+ throw error;
379
+ }
380
+ };
381
+
382
+ // Helper functions
383
+ export const getUsers = () => {
384
+ const db = readDB();
385
+ return db.users || [];
386
+ };
387
+
388
+ export const addUser = (user: any) => {
389
+ const db = readDB();
390
+ if (!db.users) db.users = [];
391
+ const newUser = { ...user, id: db.users.length + 1, createdAt: new Date().toISOString() };
392
+ db.users.push(newUser);
393
+ writeDB(db);
394
+ return newUser;
395
+ };
396
+
397
+ export const getUserById = (id: number) => {
398
+ const db = readDB();
399
+ return db.users?.find((u: any) => u.id === id);
400
+ };
401
+
402
+ export const updateUser = (id: number, updates: any) => {
403
+ const db = readDB();
404
+ const userIndex = db.users?.findIndex((u: any) => u.id === id);
405
+ if (userIndex !== -1 && userIndex !== undefined) {
406
+ db.users[userIndex] = { ...db.users[userIndex], ...updates, updatedAt: new Date().toISOString() };
407
+ writeDB(db);
408
+ return db.users[userIndex];
409
+ }
410
+ return null;
411
+ };
412
+
413
+ export const deleteUser = (id: number) => {
414
+ const db = readDB();
415
+ db.users = db.users?.filter((u: any) => u.id !== id) || [];
416
+ writeDB(db);
417
+ return true;
418
+ };
419
+
420
+ export default { readDB, writeDB, getUsers, addUser, getUserById, updateUser, deleteUser };
421
+ `;
422
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'db.ts'), dbContent);
423
+ // Create initial data directory and file
424
+ const dataPath = path_1.default.join(backendPath, 'data');
425
+ await fs_extra_1.default.ensureDir(dataPath);
426
+ await fs_extra_1.default.writeJSON(path_1.default.join(dataPath, 'database.json'), { users: [] }, { spaces: 2 });
427
+ }
@@ -15,7 +15,10 @@ async function generateFastAPI(backendPath, config) {
15
15
  'pydantic-settings==2.6.1'
16
16
  ];
17
17
  // Add database dependencies
18
- if (config.storage === 'local-sqlite') {
18
+ if (config.storage === 'local-json') {
19
+ // No additional dependencies needed for JSON file storage (uses built-in json module)
20
+ }
21
+ else if (config.storage === 'local-sqlite') {
19
22
  if (config.databaseType === 'sql') {
20
23
  dependencies.push('sqlalchemy==2.0.36');
21
24
  }
@@ -81,7 +84,10 @@ if __name__ == "__main__":
81
84
  }
82
85
  await fs_extra_1.default.writeFile(path_1.default.join(backendPath, '.env.example'), envExample);
83
86
  // Create database setup if needed
84
- if (config.databaseType === 'sql') {
87
+ if (config.storage === 'local-json') {
88
+ await generateJSONFileSetup(srcPath, config);
89
+ }
90
+ else if (config.databaseType === 'sql') {
85
91
  await generateSQLAlchemySetup(srcPath, config);
86
92
  }
87
93
  else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
@@ -171,3 +177,83 @@ users_table = dynamodb.Table('users')
171
177
  `;
172
178
  await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'database.py'), dbContent);
173
179
  }
180
+ async function generateJSONFileSetup(srcPath, config) {
181
+ const backendPath = path_1.default.dirname(path_1.default.dirname(srcPath));
182
+ const dbContent = `import json
183
+ import os
184
+ from pathlib import Path
185
+ from typing import Dict, List, Any, Optional
186
+
187
+ DB_FILE = Path(__file__).parent.parent / 'data' / 'database.json'
188
+
189
+ # Ensure data directory exists
190
+ DB_FILE.parent.mkdir(parents=True, exist_ok=True)
191
+
192
+ # Initialize database file if it doesn't exist
193
+ if not DB_FILE.exists():
194
+ with open(DB_FILE, 'w') as f:
195
+ json.dump({'users': []}, f, indent=2)
196
+
197
+ def read_db() -> Dict[str, Any]:
198
+ """Read the database from JSON file."""
199
+ try:
200
+ with open(DB_FILE, 'r') as f:
201
+ return json.load(f)
202
+ except (FileNotFoundError, json.JSONDecodeError):
203
+ return {'users': []}
204
+
205
+ def write_db(data: Dict[str, Any]) -> None:
206
+ """Write data to the database JSON file."""
207
+ with open(DB_FILE, 'w') as f:
208
+ json.dump(data, f, indent=2)
209
+
210
+ def get_users() -> List[Dict[str, Any]]:
211
+ """Get all users from the database."""
212
+ db = read_db()
213
+ return db.get('users', [])
214
+
215
+ def add_user(user: Dict[str, Any]) -> Dict[str, Any]:
216
+ """Add a new user to the database."""
217
+ db = read_db()
218
+ users = db.get('users', [])
219
+ new_user = {
220
+ **user,
221
+ 'id': len(users) + 1,
222
+ 'created_at': str(Path(__file__).stat().st_mtime)
223
+ }
224
+ users.append(new_user)
225
+ db['users'] = users
226
+ write_db(db)
227
+ return new_user
228
+
229
+ def get_user_by_id(user_id: int) -> Optional[Dict[str, Any]]:
230
+ """Get a user by ID."""
231
+ users = get_users()
232
+ return next((u for u in users if u.get('id') == user_id), None)
233
+
234
+ def update_user(user_id: int, updates: Dict[str, Any]) -> Optional[Dict[str, Any]]:
235
+ """Update a user by ID."""
236
+ db = read_db()
237
+ users = db.get('users', [])
238
+ for i, user in enumerate(users):
239
+ if user.get('id') == user_id:
240
+ users[i] = {**user, **updates, 'updated_at': str(Path(__file__).stat().st_mtime)}
241
+ db['users'] = users
242
+ write_db(db)
243
+ return users[i]
244
+ return None
245
+
246
+ def delete_user(user_id: int) -> bool:
247
+ """Delete a user by ID."""
248
+ db = read_db()
249
+ users = db.get('users', [])
250
+ db['users'] = [u for u in users if u.get('id') != user_id]
251
+ write_db(db)
252
+ return True
253
+ `;
254
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'database.py'), dbContent);
255
+ // Create initial data directory and file
256
+ const dataPath = path_1.default.join(backendPath, 'data');
257
+ await fs_extra_1.default.ensureDir(dataPath);
258
+ await fs_extra_1.default.writeJSON(path_1.default.join(dataPath, 'database.json'), { users: [] }, { spaces: 2 });
259
+ }
@@ -27,7 +27,10 @@ async function generateNestJS(backendPath, config) {
27
27
  typescript: '^5.6.3'
28
28
  };
29
29
  // Add database dependencies
30
- if (config.storage === 'local-sqlite') {
30
+ if (config.storage === 'local-json') {
31
+ // No additional dependencies needed for JSON file storage
32
+ }
33
+ else if (config.storage === 'local-sqlite') {
31
34
  if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
32
35
  dependencies['@prisma/client'] = '^6.0.1';
33
36
  dependencies['prisma'] = '^6.0.1';
@@ -291,7 +294,10 @@ describe('AppService', () => {
291
294
  `;
292
295
  await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'app.service.spec.ts'), appServiceTest);
293
296
  // Create database setup if needed
294
- if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
297
+ if (config.storage === 'local-json') {
298
+ await generateJSONFileModule(srcPath, config);
299
+ }
300
+ else if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
295
301
  await generatePrismaConfig(backendPath, config);
296
302
  }
297
303
  else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
@@ -370,6 +376,90 @@ export const UserSchema = SchemaFactory.createForClass(User);
370
376
  `;
371
377
  await fs_extra_1.default.writeFile(path_1.default.join(schemasPath, 'user.schema.ts'), userSchema);
372
378
  }
379
+ async function generateJSONFileModule(srcPath, config) {
380
+ const backendPath = path_1.default.dirname(path_1.default.dirname(srcPath));
381
+ // Create JSON service
382
+ const jsonService = `import { Injectable } from '@nestjs/common';
383
+ import * as fs from 'fs-extra';
384
+ import * as path from 'path';
385
+
386
+ const DB_FILE = path.join(process.cwd(), 'data', 'database.json');
387
+
388
+ @Injectable()
389
+ export class JsonDatabaseService {
390
+ private ensureDataDir(): void {
391
+ const dataDir = path.dirname(DB_FILE);
392
+ fs.ensureDirSync(dataDir);
393
+ if (!fs.existsSync(DB_FILE)) {
394
+ fs.writeJSONSync(DB_FILE, { users: [] }, { spaces: 2 });
395
+ }
396
+ }
397
+
398
+ private readDB(): any {
399
+ this.ensureDataDir();
400
+ try {
401
+ return fs.readJSONSync(DB_FILE);
402
+ } catch (error) {
403
+ console.error('Error reading database:', error);
404
+ return { users: [] };
405
+ }
406
+ }
407
+
408
+ private writeDB(data: any): void {
409
+ try {
410
+ fs.writeJSONSync(DB_FILE, data, { spaces: 2 });
411
+ } catch (error) {
412
+ console.error('Error writing database:', error);
413
+ throw error;
414
+ }
415
+ }
416
+
417
+ getUsers(): any[] {
418
+ const db = this.readDB();
419
+ return db.users || [];
420
+ }
421
+
422
+ addUser(user: any): any {
423
+ const db = this.readDB();
424
+ if (!db.users) db.users = [];
425
+ const newUser = { ...user, id: db.users.length + 1, createdAt: new Date().toISOString() };
426
+ db.users.push(newUser);
427
+ this.writeDB(db);
428
+ return newUser;
429
+ }
430
+
431
+ getUserById(id: number): any {
432
+ const db = this.readDB();
433
+ return db.users?.find((u: any) => u.id === id);
434
+ }
435
+
436
+ updateUser(id: number, updates: any): any {
437
+ const db = this.readDB();
438
+ const userIndex = db.users?.findIndex((u: any) => u.id === id);
439
+ if (userIndex !== -1 && userIndex !== undefined) {
440
+ db.users[userIndex] = { ...db.users[userIndex], ...updates, updatedAt: new Date().toISOString() };
441
+ this.writeDB(db);
442
+ return db.users[userIndex];
443
+ }
444
+ return null;
445
+ }
446
+
447
+ deleteUser(id: number): boolean {
448
+ const db = this.readDB();
449
+ db.users = db.users?.filter((u: any) => u.id !== id) || [];
450
+ this.writeDB(db);
451
+ return true;
452
+ }
453
+ }
454
+ `;
455
+ const servicesPath = path_1.default.join(srcPath, 'services');
456
+ await fs_extra_1.default.ensureDir(servicesPath);
457
+ await fs_extra_1.default.writeFile(path_1.default.join(servicesPath, 'json-database.service.ts'), jsonService);
458
+ // Create initial data directory and file
459
+ const dataPath = path_1.default.join(backendPath, 'data');
460
+ await fs_extra_1.default.ensureDir(dataPath);
461
+ await fs_extra_1.default.writeJSON(path_1.default.join(dataPath, 'database.json'), { users: [] }, { spaces: 2 });
462
+ }
373
463
  async function generateBackendDeployment(backendPath, config) {
374
464
  // Render configuration
375
465
  const renderYaml = `services:
@@ -116,6 +116,10 @@ __pycache__/
116
116
  venv/
117
117
  cdk.out/
118
118
  .sam/
119
+ data/
120
+ *.db
121
+ *.sqlite
122
+ *.sqlite3
119
123
  `;
120
124
  await fs_extra_1.default.writeFile(path_1.default.join(projectPath, '.gitignore'), gitignore);
121
125
  // Generate CI/CD workflows
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "projects-init-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "CLI tool to initialize projects with customizable frontend, backend, and storage options",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
- "projects-init": "./dist/index.js"
7
+ "projects-init": "dist/index.js"
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc",
@@ -31,19 +31,22 @@
31
31
  ],
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "https://github.com/yourusername/projects-init-cli.git"
34
+ "url": "git+https://github.com/MacAndersonUche/Projects-Init-CLI.git"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
35
38
  },
36
39
  "engines": {
37
40
  "node": ">=18.0.0"
38
41
  },
39
42
  "dependencies": {
40
43
  "commander": "^11.1.0",
41
- "inquirer": "^9.2.12",
44
+ "inquirer": "^8.2.6",
42
45
  "chalk": "^4.1.2",
43
46
  "fs-extra": "^11.2.0"
44
47
  },
45
48
  "devDependencies": {
46
- "@types/inquirer": "^9.0.7",
49
+ "@types/inquirer": "^8.2.10",
47
50
  "@types/node": "^20.10.6",
48
51
  "@types/fs-extra": "^11.0.4",
49
52
  "typescript": "^5.3.3",