projects-init-cli 2.0.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.
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateExpress = generateExpress;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateExpress(backendPath, config) {
10
+ const dependencies = {
11
+ express: '^4.21.1',
12
+ cors: '^2.8.5',
13
+ dotenv: '^16.4.5'
14
+ };
15
+ const devDependencies = {
16
+ '@types/express': '^5.0.0',
17
+ '@types/cors': '^2.8.17',
18
+ '@types/node': '^22.7.5',
19
+ typescript: '^5.6.3',
20
+ 'ts-node': '^10.9.2',
21
+ nodemon: '^3.1.7',
22
+ 'vitest': '^2.1.3',
23
+ '@vitest/ui': '^2.1.3',
24
+ 'supertest': '^7.0.0',
25
+ '@types/supertest': '^6.0.2',
26
+ '@vitest/coverage-v8': '^2.1.3'
27
+ };
28
+ // Add database dependencies
29
+ if (config.storage === 'local-sqlite') {
30
+ if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
31
+ dependencies['@prisma/client'] = '^6.0.1';
32
+ devDependencies['prisma'] = '^6.0.1';
33
+ }
34
+ else if (config.databaseType === 'sql') {
35
+ dependencies['better-sqlite3'] = '^11.7.0';
36
+ devDependencies['@types/better-sqlite3'] = '^7.6.9';
37
+ }
38
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
39
+ dependencies['mongoose'] = '^8.8.4';
40
+ }
41
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'dynamodb') {
42
+ dependencies['@aws-sdk/client-dynamodb'] = '^3.699.0';
43
+ dependencies['@aws-sdk/lib-dynamodb'] = '^3.699.0';
44
+ }
45
+ }
46
+ else if (config.storage === 'external-url') {
47
+ if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
48
+ dependencies['@prisma/client'] = '^6.0.1';
49
+ devDependencies['prisma'] = '^6.0.1';
50
+ }
51
+ else if (config.databaseType === 'sql') {
52
+ dependencies['pg'] = '^8.13.1';
53
+ devDependencies['@types/pg'] = '^8.11.10';
54
+ }
55
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
56
+ dependencies['mongoose'] = '^8.8.4';
57
+ }
58
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'dynamodb') {
59
+ dependencies['@aws-sdk/client-dynamodb'] = '^3.699.0';
60
+ dependencies['@aws-sdk/lib-dynamodb'] = '^3.699.0';
61
+ }
62
+ }
63
+ // Add API type dependencies
64
+ if (config.apiType === 'graphql') {
65
+ dependencies['graphql'] = '^16.9.0';
66
+ dependencies['express-graphql'] = '^0.12.0';
67
+ devDependencies['@types/express-graphql'] = '^0.12.0';
68
+ }
69
+ const packageJson = {
70
+ name: `${config.projectName}-backend`,
71
+ version: '1.0.0',
72
+ main: 'dist/index.js',
73
+ scripts: {
74
+ dev: 'nodemon --exec ts-node src/index.ts',
75
+ build: 'tsc',
76
+ start: 'node dist/index.js',
77
+ test: 'vitest',
78
+ 'test:ui': 'vitest --ui',
79
+ 'test:coverage': 'vitest --coverage'
80
+ },
81
+ dependencies,
82
+ devDependencies
83
+ };
84
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'package.json'), packageJson, { spaces: 2 });
85
+ // Create TypeScript config
86
+ const tsconfig = {
87
+ compilerOptions: {
88
+ target: 'ES2020',
89
+ module: 'commonjs',
90
+ lib: ['ES2020'],
91
+ outDir: './dist',
92
+ rootDir: './src',
93
+ strict: true,
94
+ esModuleInterop: true,
95
+ skipLibCheck: true,
96
+ forceConsistentCasingInFileNames: true,
97
+ resolveJsonModule: true,
98
+ moduleResolution: 'node'
99
+ },
100
+ include: ['src/**/*'],
101
+ exclude: ['node_modules', 'dist']
102
+ };
103
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'tsconfig.json'), tsconfig, { spaces: 2 });
104
+ // Create Vitest config
105
+ const vitestConfig = `import { defineConfig } from 'vitest/config'
106
+
107
+ export default defineConfig({
108
+ test: {
109
+ globals: true,
110
+ environment: 'node',
111
+ coverage: {
112
+ provider: 'v8',
113
+ reporter: ['text', 'json', 'html'],
114
+ },
115
+ },
116
+ })
117
+ `;
118
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'vitest.config.ts'), vitestConfig);
119
+ // Create src directory
120
+ const srcPath = path_1.default.join(backendPath, 'src');
121
+ await fs_extra_1.default.ensureDir(srcPath);
122
+ // Create index.ts
123
+ let indexContent = `import express from 'express';
124
+ import cors from 'cors';
125
+ import dotenv from 'dotenv';
126
+
127
+ dotenv.config();
128
+
129
+ const app = express();
130
+ const PORT = process.env.PORT || 3001;
131
+
132
+ app.use(cors());
133
+ app.use(express.json());
134
+
135
+ // Health check endpoint
136
+ app.get('/health', (req, res) => {
137
+ res.json({ status: 'ok', message: 'Server is running' });
138
+ });
139
+
140
+ `;
141
+ if (config.apiType === 'graphql') {
142
+ indexContent += `import { graphqlHTTP } from 'express-graphql';
143
+ import { buildSchema } from 'graphql';
144
+
145
+ // GraphQL schema
146
+ const schema = buildSchema(\`
147
+ type Query {
148
+ hello: String
149
+ }
150
+ \`);
151
+
152
+ // Root resolver
153
+ const root = {
154
+ hello: () => 'Hello from GraphQL!',
155
+ };
156
+
157
+ app.use('/graphql', graphqlHTTP({
158
+ schema: schema,
159
+ rootValue: root,
160
+ graphiql: true,
161
+ }));
162
+
163
+ `;
164
+ }
165
+ else {
166
+ indexContent += `// REST API routes
167
+ app.get('/api', (req, res) => {
168
+ res.json({ message: 'Welcome to the REST API' });
169
+ });
170
+
171
+ `;
172
+ }
173
+ indexContent += `const server = app.listen(PORT, () => {
174
+ console.log(\`Server is running on http://localhost:\${PORT}\`);
175
+ });
176
+
177
+ export default app;
178
+ `;
179
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'index.ts'), indexContent);
180
+ // Create .env.example
181
+ let envExample = `PORT=3001
182
+ `;
183
+ if (config.storage === 'external-url' && config.databaseUrl) {
184
+ envExample += `DATABASE_URL=${config.databaseUrl}\n`;
185
+ }
186
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, '.env.example'), envExample);
187
+ // Create database setup if needed
188
+ if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
189
+ await generatePrismaConfig(backendPath, config);
190
+ }
191
+ else if (config.databaseType === 'sql' && config.storage === 'local-sqlite') {
192
+ await generateSQLiteSetup(srcPath, config);
193
+ }
194
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
195
+ await generateMongoDBSetup(srcPath, config);
196
+ }
197
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'dynamodb') {
198
+ await generateDynamoDBSetup(srcPath, config);
199
+ }
200
+ // Create test files
201
+ const testPath = path_1.default.join(srcPath, '__tests__');
202
+ await fs_extra_1.default.ensureDir(testPath);
203
+ const testFile = `import { describe, it, expect, beforeAll, afterAll } from 'vitest'
204
+ import request from 'supertest'
205
+ import app from '../index'
206
+
207
+ describe('API Tests', () => {
208
+ it('should return health check', async () => {
209
+ const res = await request(app).get('/health')
210
+ expect(res.status).toBe(200)
211
+ expect(res.body.status).toBe('ok')
212
+ })
213
+
214
+ ${config.apiType === 'rest' ? ` it('should return API welcome message', async () => {
215
+ const res = await request(app).get('/api')
216
+ expect(res.status).toBe(200)
217
+ expect(res.body.message).toContain('REST API')
218
+ })` : ` it('should handle GraphQL query', async () => {
219
+ const res = await request(app)
220
+ .post('/graphql')
221
+ .send({ query: '{ hello }' })
222
+ expect(res.status).toBe(200)
223
+ expect(res.body.data.hello).toBe('Hello from GraphQL!')
224
+ })`}
225
+ })
226
+ `;
227
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'api.test.ts'), testFile);
228
+ // Create deployment files
229
+ await generateBackendDeployment(backendPath, config);
230
+ }
231
+ async function generateBackendDeployment(backendPath, config) {
232
+ // Render configuration
233
+ const renderYaml = `services:
234
+ - type: web
235
+ name: ${config.projectName}-backend
236
+ env: node
237
+ buildCommand: npm install && npm run build
238
+ startCommand: npm start
239
+ envVars:
240
+ - key: NODE_ENV
241
+ value: production
242
+ - key: PORT
243
+ value: 3001
244
+ `;
245
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'render.yaml'), renderYaml);
246
+ }
247
+ async function generatePrismaConfig(backendPath, config) {
248
+ const prismaPath = path_1.default.join(backendPath, 'prisma');
249
+ await fs_extra_1.default.ensureDir(prismaPath);
250
+ let datasource = '';
251
+ if (config.storage === 'local-sqlite') {
252
+ datasource = `datasource db {
253
+ provider = "sqlite"
254
+ url = "file:./dev.db"
255
+ }`;
256
+ }
257
+ else if (config.storage === 'external-url') {
258
+ datasource = `datasource db {
259
+ provider = "postgresql"
260
+ url = env("DATABASE_URL")
261
+ }`;
262
+ }
263
+ const schema = `${datasource}
264
+
265
+ generator client {
266
+ provider = "prisma-client-js"
267
+ }
268
+
269
+ model User {
270
+ id Int @id @default(autoincrement())
271
+ email String @unique
272
+ name String?
273
+ createdAt DateTime @default(now())
274
+ updatedAt DateTime @updatedAt
275
+ }
276
+ `;
277
+ await fs_extra_1.default.writeFile(path_1.default.join(prismaPath, 'schema.prisma'), schema);
278
+ }
279
+ async function generateSQLiteSetup(srcPath, config) {
280
+ const dbContent = `import Database from 'better-sqlite3';
281
+ import path from 'path';
282
+
283
+ const dbPath = path.join(process.cwd(), 'database.db');
284
+ const db = new Database(dbPath);
285
+
286
+ // Initialize database
287
+ db.exec(\`
288
+ CREATE TABLE IF NOT EXISTS users (
289
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
290
+ email TEXT UNIQUE NOT NULL,
291
+ name TEXT,
292
+ createdAt DATETIME DEFAULT CURRENT_TIMESTAMP
293
+ )
294
+ \`);
295
+
296
+ export default db;
297
+ `;
298
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'db.ts'), dbContent);
299
+ }
300
+ async function generateMongoDBSetup(srcPath, config) {
301
+ const dbContent = `import mongoose from 'mongoose';
302
+
303
+ const connectDB = async () => {
304
+ try {
305
+ const mongoURI = process.env.DATABASE_URL || 'mongodb://localhost:27017/${config.projectName}';
306
+ await mongoose.connect(mongoURI);
307
+ console.log('MongoDB connected');
308
+ } catch (error) {
309
+ console.error('MongoDB connection error:', error);
310
+ process.exit(1);
311
+ }
312
+ };
313
+
314
+ // User schema example
315
+ const userSchema = new mongoose.Schema({
316
+ email: { type: String, required: true, unique: true },
317
+ name: String,
318
+ createdAt: { type: Date, default: Date.now }
319
+ });
320
+
321
+ export const User = mongoose.model('User', userSchema);
322
+ export default connectDB;
323
+ `;
324
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'db.ts'), dbContent);
325
+ }
326
+ async function generateDynamoDBSetup(srcPath, config) {
327
+ const dbContent = `import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
328
+ import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
329
+
330
+ const client = new DynamoDBClient({
331
+ region: process.env.AWS_REGION || 'us-east-1',
332
+ });
333
+
334
+ export const docClient = DynamoDBDocumentClient.from(client);
335
+
336
+ export default docClient;
337
+ `;
338
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'db.ts'), dbContent);
339
+ }
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateFastAPI = generateFastAPI;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateFastAPI(backendPath, config) {
10
+ const dependencies = [
11
+ 'fastapi==0.115.6',
12
+ 'uvicorn[standard]==0.32.1',
13
+ 'python-dotenv==1.0.1',
14
+ 'pydantic==2.10.3',
15
+ 'pydantic-settings==2.6.1'
16
+ ];
17
+ // Add database dependencies
18
+ if (config.storage === 'local-sqlite') {
19
+ if (config.databaseType === 'sql') {
20
+ dependencies.push('sqlalchemy==2.0.36');
21
+ }
22
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
23
+ dependencies.push('motor==3.6.0');
24
+ dependencies.push('pymongo==4.10.1');
25
+ }
26
+ }
27
+ else if (config.storage === 'external-url') {
28
+ if (config.databaseType === 'sql') {
29
+ dependencies.push('sqlalchemy==2.0.36');
30
+ dependencies.push('psycopg2-binary==2.9.10');
31
+ }
32
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
33
+ dependencies.push('motor==3.6.0');
34
+ dependencies.push('pymongo==4.10.1');
35
+ }
36
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'dynamodb') {
37
+ dependencies.push('boto3==1.35.47');
38
+ }
39
+ }
40
+ // Create requirements.txt
41
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'requirements.txt'), dependencies.join('\n'));
42
+ // Create main.py
43
+ const srcPath = path_1.default.join(backendPath, 'src');
44
+ await fs_extra_1.default.ensureDir(srcPath);
45
+ let mainContent = `from fastapi import FastAPI
46
+ from fastapi.middleware.cors import CORSMiddleware
47
+ from dotenv import load_dotenv
48
+ import os
49
+
50
+ load_dotenv()
51
+
52
+ app = FastAPI(title="${config.projectName} API")
53
+
54
+ app.add_middleware(
55
+ CORSMiddleware,
56
+ allow_origins=["*"],
57
+ allow_credentials=True,
58
+ allow_methods=["*"],
59
+ allow_headers=["*"],
60
+ )
61
+
62
+ @app.get("/")
63
+ def read_root():
64
+ return {"message": "Welcome to ${config.projectName} API"}
65
+
66
+ @app.get("/health")
67
+ def health_check():
68
+ return {"status": "ok", "message": "Server is running"}
69
+
70
+ if __name__ == "__main__":
71
+ import uvicorn
72
+ port = int(os.getenv("PORT", 3001))
73
+ uvicorn.run(app, host="0.0.0.0", port=port)
74
+ `;
75
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'main.py'), mainContent);
76
+ // Create .env.example
77
+ let envExample = `PORT=3001
78
+ `;
79
+ if (config.storage === 'external-url' && config.databaseUrl) {
80
+ envExample += `DATABASE_URL=${config.databaseUrl}\n`;
81
+ }
82
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, '.env.example'), envExample);
83
+ // Create database setup if needed
84
+ if (config.databaseType === 'sql') {
85
+ await generateSQLAlchemySetup(srcPath, config);
86
+ }
87
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
88
+ await generateMongoDBSetup(srcPath, config);
89
+ }
90
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'dynamodb') {
91
+ await generateDynamoDBSetup(srcPath, config);
92
+ }
93
+ // Create deployment files
94
+ await generateBackendDeployment(backendPath, config);
95
+ }
96
+ async function generateBackendDeployment(backendPath, config) {
97
+ // Render configuration
98
+ const renderYaml = `services:
99
+ - type: web
100
+ name: ${config.projectName}-backend
101
+ env: python
102
+ buildCommand: pip install -r requirements.txt
103
+ startCommand: uvicorn src.main:app --host 0.0.0.0 --port \$PORT
104
+ envVars:
105
+ - key: PORT
106
+ value: 3001
107
+ `;
108
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'render.yaml'), renderYaml);
109
+ }
110
+ async function generateSQLAlchemySetup(srcPath, config) {
111
+ const dbUrlLine = config.storage === 'local-sqlite'
112
+ ? 'DATABASE_URL = "sqlite:///./database.db"'
113
+ : 'DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@localhost/dbname")';
114
+ const dbContent = `from sqlalchemy import create_engine, Column, Integer, String, DateTime
115
+ from sqlalchemy.ext.declarative import declarative_base
116
+ from sqlalchemy.orm import sessionmaker
117
+ import os
118
+ from datetime import datetime
119
+
120
+ ${dbUrlLine}
121
+
122
+ engine = create_engine(DATABASE_URL)
123
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
124
+ Base = declarative_base()
125
+
126
+ class User(Base):
127
+ __tablename__ = "users"
128
+
129
+ id = Column(Integer, primary_key=True, index=True)
130
+ email = Column(String, unique=True, index=True)
131
+ name = Column(String, nullable=True)
132
+ created_at = Column(DateTime, default=datetime.utcnow)
133
+
134
+ Base.metadata.create_all(bind=engine)
135
+
136
+ def get_db():
137
+ db = SessionLocal()
138
+ try:
139
+ yield db
140
+ finally:
141
+ db.close()
142
+ `;
143
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'database.py'), dbContent);
144
+ }
145
+ async function generateMongoDBSetup(srcPath, config) {
146
+ const dbContent = `from motor.motor_asyncio import AsyncIOMotorClient
147
+ import os
148
+
149
+ DATABASE_URL = os.getenv("DATABASE_URL", "mongodb://localhost:27017")
150
+ DATABASE_NAME = "${config.projectName}"
151
+
152
+ client = AsyncIOMotorClient(DATABASE_URL)
153
+ db = client[DATABASE_NAME]
154
+
155
+ # Collections
156
+ users_collection = db["users"]
157
+ `;
158
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'database.py'), dbContent);
159
+ }
160
+ async function generateDynamoDBSetup(srcPath, config) {
161
+ const dbContent = `import boto3
162
+ import os
163
+
164
+ dynamodb = boto3.resource(
165
+ 'dynamodb',
166
+ region_name=os.getenv('AWS_REGION', 'us-east-1')
167
+ )
168
+
169
+ # Table references
170
+ users_table = dynamodb.Table('users')
171
+ `;
172
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'database.py'), dbContent);
173
+ }