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
|
|
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:
|
|
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-
|
|
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.
|
|
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-
|
|
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.
|
|
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-
|
|
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.
|
|
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:
|
package/dist/templates/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projects-init-cli",
|
|
3
|
-
"version": "2.
|
|
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": "
|
|
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/
|
|
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": "^
|
|
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": "^
|
|
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",
|