projects-init-cli 1.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.
@@ -0,0 +1,478 @@
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.generateNestJS = generateNestJS;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateNestJS(backendPath, config) {
10
+ const dependencies = {
11
+ '@nestjs/common': '^10.4.8',
12
+ '@nestjs/core': '^10.4.8',
13
+ '@nestjs/platform-express': '^10.4.8',
14
+ '@nestjs/testing': '^10.4.8',
15
+ 'reflect-metadata': '^0.2.2',
16
+ 'rxjs': '^7.8.1'
17
+ };
18
+ const devDependencies = {
19
+ '@nestjs/cli': '^10.4.5',
20
+ '@nestjs/schematics': '^10.1.1',
21
+ '@types/express': '^5.0.0',
22
+ '@types/node': '^22.7.5',
23
+ 'source-map-support': '^0.5.21',
24
+ 'ts-loader': '^9.5.1',
25
+ 'ts-node': '^10.9.2',
26
+ 'tsconfig-paths': '^4.2.0',
27
+ typescript: '^5.6.3'
28
+ };
29
+ // Add database dependencies
30
+ if (config.storage === 'local-json') {
31
+ // No additional dependencies needed for JSON file storage
32
+ }
33
+ else if (config.storage === 'local-sqlite') {
34
+ if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
35
+ dependencies['@prisma/client'] = '^6.0.1';
36
+ dependencies['prisma'] = '^6.0.1';
37
+ }
38
+ else if (config.databaseType === 'sql') {
39
+ dependencies['typeorm'] = '^0.3.20';
40
+ dependencies['better-sqlite3'] = '^11.7.0';
41
+ devDependencies['@types/better-sqlite3'] = '^7.6.9';
42
+ }
43
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
44
+ dependencies['@nestjs/mongoose'] = '^10.1.0';
45
+ dependencies['mongoose'] = '^8.8.4';
46
+ }
47
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'dynamodb') {
48
+ dependencies['@aws-sdk/client-dynamodb'] = '^3.699.0';
49
+ dependencies['@aws-sdk/lib-dynamodb'] = '^3.699.0';
50
+ }
51
+ }
52
+ else if (config.storage === 'external-url') {
53
+ if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
54
+ dependencies['@prisma/client'] = '^6.0.1';
55
+ dependencies['prisma'] = '^6.0.1';
56
+ }
57
+ else if (config.databaseType === 'sql') {
58
+ dependencies['typeorm'] = '^0.3.20';
59
+ dependencies['pg'] = '^8.13.1';
60
+ devDependencies['@types/pg'] = '^8.11.10';
61
+ }
62
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
63
+ dependencies['@nestjs/mongoose'] = '^10.1.0';
64
+ dependencies['mongoose'] = '^8.8.4';
65
+ }
66
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'dynamodb') {
67
+ dependencies['@aws-sdk/client-dynamodb'] = '^3.699.0';
68
+ dependencies['@aws-sdk/lib-dynamodb'] = '^3.699.0';
69
+ }
70
+ }
71
+ // Add API type dependencies
72
+ if (config.apiType === 'graphql') {
73
+ dependencies['@nestjs/graphql'] = '^12.1.0';
74
+ dependencies['@nestjs/apollo'] = '^12.1.0';
75
+ dependencies['apollo-server-express'] = '^3.12.1';
76
+ dependencies['graphql'] = '^16.9.0';
77
+ }
78
+ const packageJson = {
79
+ name: `${config.projectName}-backend`,
80
+ version: '1.0.0',
81
+ description: '',
82
+ author: '',
83
+ private: true,
84
+ license: 'MIT',
85
+ scripts: {
86
+ build: 'nest build',
87
+ format: 'prettier --write "src/**/*.ts" "test/**/*.ts"',
88
+ start: 'nest start',
89
+ 'start:dev': 'nest start --watch',
90
+ 'start:debug': 'nest start --debug --watch',
91
+ 'start:prod': 'node dist/main',
92
+ lint: 'eslint "{src,apps,libs,test}/**/*.ts" --fix',
93
+ test: 'vitest',
94
+ 'test:ui': 'vitest --ui',
95
+ 'test:coverage': 'vitest --coverage',
96
+ 'test:e2e': 'vitest --config vitest.e2e.config.ts'
97
+ },
98
+ dependencies,
99
+ devDependencies: {
100
+ ...devDependencies,
101
+ '@typescript-eslint/eslint-plugin': '^8.15.0',
102
+ '@typescript-eslint/parser': '^8.15.0',
103
+ eslint: '^9.15.0',
104
+ 'eslint-config-prettier': '^9.1.0',
105
+ 'eslint-plugin-prettier': '^6.0.0',
106
+ prettier: '^3.3.3',
107
+ 'vitest': '^2.1.3',
108
+ '@vitest/ui': '^2.1.3',
109
+ '@vitest/coverage-v8': '^2.1.3'
110
+ }
111
+ };
112
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'package.json'), packageJson, { spaces: 2 });
113
+ // Create nest-cli.json
114
+ const nestCli = {
115
+ '$schema': 'https://json.schemastore.org/nest-cli',
116
+ collection: '@nestjs/schematics',
117
+ sourceRoot: 'src',
118
+ compilerOptions: {
119
+ deleteOutDir: true
120
+ }
121
+ };
122
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'nest-cli.json'), nestCli, { spaces: 2 });
123
+ // Create tsconfig
124
+ const tsconfig = {
125
+ compilerOptions: {
126
+ module: 'commonjs',
127
+ declaration: true,
128
+ removeComments: true,
129
+ emitDecoratorMetadata: true,
130
+ experimentalDecorators: true,
131
+ allowSyntheticDefaultImports: true,
132
+ target: 'ES2021',
133
+ sourceMap: true,
134
+ outDir: './dist',
135
+ baseUrl: './',
136
+ incremental: true,
137
+ skipLibCheck: true,
138
+ strictNullChecks: false,
139
+ noImplicitAny: false,
140
+ strictBindCallApply: false,
141
+ forceConsistentCasingInFileNames: false,
142
+ noFallthroughCasesInSwitch: false
143
+ }
144
+ };
145
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'tsconfig.json'), tsconfig, { spaces: 2 });
146
+ // Create src directory structure
147
+ const srcPath = path_1.default.join(backendPath, 'src');
148
+ await fs_extra_1.default.ensureDir(srcPath);
149
+ // Create main.ts
150
+ let mainContent = `import { NestFactory } from '@nestjs/core';
151
+ import { AppModule } from './app.module';
152
+
153
+ async function bootstrap() {
154
+ const app = await NestFactory.create(AppModule);
155
+ app.enableCors();
156
+ await app.listen(process.env.PORT || 3001);
157
+ console.log(\`Application is running on: http://localhost:\${process.env.PORT || 3001}\`);
158
+ }
159
+ bootstrap();
160
+ `;
161
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'main.ts'), mainContent);
162
+ // Create app.module.ts
163
+ let appModuleContent = `import { Module } from '@nestjs/common';
164
+ import { AppController } from './app.controller';
165
+ import { AppService } from './app.service';
166
+ `;
167
+ if (config.apiType === 'graphql') {
168
+ appModuleContent += `import { GraphQLModule } from '@nestjs/graphql';
169
+ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
170
+ import { join } from 'path';
171
+
172
+ `;
173
+ }
174
+ appModuleContent += `
175
+ @Module({
176
+ imports: [
177
+ `;
178
+ if (config.apiType === 'graphql') {
179
+ appModuleContent += ` GraphQLModule.forRoot<ApolloDriverConfig>({
180
+ driver: ApolloDriver,
181
+ autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
182
+ sortSchema: true,
183
+ }),
184
+ `;
185
+ }
186
+ appModuleContent += ` ],
187
+ controllers: [AppController],
188
+ providers: [AppService],
189
+ })
190
+ export class AppModule {}
191
+ `;
192
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'app.module.ts'), appModuleContent);
193
+ // Create app.controller.ts
194
+ const appController = `import { Controller, Get } from '@nestjs/common';
195
+ import { AppService } from './app.service';
196
+
197
+ @Controller()
198
+ export class AppController {
199
+ constructor(private readonly appService: AppService) {}
200
+
201
+ @Get()
202
+ getHello(): string {
203
+ return this.appService.getHello();
204
+ }
205
+
206
+ @Get('health')
207
+ getHealth() {
208
+ return { status: 'ok', message: 'Server is running' };
209
+ }
210
+ }
211
+ `;
212
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'app.controller.ts'), appController);
213
+ // Create app.service.ts
214
+ const appService = `import { Injectable } from '@nestjs/common';
215
+
216
+ @Injectable()
217
+ export class AppService {
218
+ getHello(): string {
219
+ return 'Hello World!';
220
+ }
221
+ }
222
+ `;
223
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'app.service.ts'), appService);
224
+ // Create .env.example
225
+ let envExample = `PORT=3001
226
+ `;
227
+ if (config.storage === 'external-url' && config.databaseUrl) {
228
+ envExample += `DATABASE_URL=${config.databaseUrl}\n`;
229
+ }
230
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, '.env.example'), envExample);
231
+ // Create Vitest config
232
+ const vitestConfig = `import { defineConfig } from 'vitest/config'
233
+ import path from 'path'
234
+
235
+ export default defineConfig({
236
+ test: {
237
+ globals: true,
238
+ environment: 'node',
239
+ coverage: {
240
+ provider: 'v8',
241
+ reporter: ['text', 'json', 'html'],
242
+ },
243
+ },
244
+ resolve: {
245
+ alias: {
246
+ '@': path.resolve(__dirname, './src'),
247
+ },
248
+ },
249
+ })
250
+ `;
251
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'vitest.config.ts'), vitestConfig);
252
+ // Create test files
253
+ const testPath = path_1.default.join(srcPath, '__tests__');
254
+ await fs_extra_1.default.ensureDir(testPath);
255
+ const appControllerTest = `import { describe, it, expect, beforeEach } from 'vitest'
256
+ import { Test, TestingModule } from '@nestjs/testing'
257
+ import { AppController } from '../app.controller'
258
+ import { AppService } from '../app.service'
259
+
260
+ describe('AppController', () => {
261
+ let appController: AppController
262
+ let appService: AppService
263
+
264
+ beforeEach(async () => {
265
+ const moduleRef: TestingModule = await Test.createTestingModule({
266
+ controllers: [AppController],
267
+ providers: [AppService],
268
+ }).compile()
269
+
270
+ appService = moduleRef.get<AppService>(AppService)
271
+ appController = moduleRef.get<AppController>(AppController)
272
+ })
273
+
274
+ it('should return "Hello World!"', () => {
275
+ expect(appController.getHello()).toBe('Hello World!')
276
+ })
277
+
278
+ it('should return health check', () => {
279
+ const result = appController.getHealth()
280
+ expect(result.status).toBe('ok')
281
+ })
282
+ })
283
+ `;
284
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'app.controller.spec.ts'), appControllerTest);
285
+ const appServiceTest = `import { describe, it, expect } from 'vitest'
286
+ import { AppService } from '../app.service'
287
+
288
+ describe('AppService', () => {
289
+ it('should return "Hello World!"', () => {
290
+ const appService = new AppService()
291
+ expect(appService.getHello()).toBe('Hello World!')
292
+ })
293
+ })
294
+ `;
295
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'app.service.spec.ts'), appServiceTest);
296
+ // Create database setup if needed
297
+ if (config.storage === 'local-json') {
298
+ await generateJSONFileModule(srcPath, config);
299
+ }
300
+ else if (config.databaseType === 'sql' && config.sqlOption === 'prisma') {
301
+ await generatePrismaConfig(backendPath, config);
302
+ }
303
+ else if (config.databaseType === 'nosql' && config.nosqlOption === 'mongodb') {
304
+ await generateMongoDBModule(srcPath, config);
305
+ }
306
+ // Create deployment files
307
+ await generateBackendDeployment(backendPath, config);
308
+ }
309
+ async function generatePrismaConfig(backendPath, config) {
310
+ const prismaPath = path_1.default.join(backendPath, 'prisma');
311
+ await fs_extra_1.default.ensureDir(prismaPath);
312
+ let datasource = '';
313
+ if (config.storage === 'local-sqlite') {
314
+ datasource = `datasource db {
315
+ provider = "sqlite"
316
+ url = "file:./dev.db"
317
+ }`;
318
+ }
319
+ else if (config.storage === 'external-url') {
320
+ datasource = `datasource db {
321
+ provider = "postgresql"
322
+ url = env("DATABASE_URL")
323
+ }`;
324
+ }
325
+ const schema = `${datasource}
326
+
327
+ generator client {
328
+ provider = "prisma-client-js"
329
+ }
330
+
331
+ model User {
332
+ id Int @id @default(autoincrement())
333
+ email String @unique
334
+ name String?
335
+ createdAt DateTime @default(now())
336
+ updatedAt DateTime @updatedAt
337
+ }
338
+ `;
339
+ await fs_extra_1.default.writeFile(path_1.default.join(prismaPath, 'schema.prisma'), schema);
340
+ }
341
+ async function generateMongoDBModule(srcPath, config) {
342
+ const dbContent = `import { Module } from '@nestjs/common';
343
+ import { MongooseModule } from '@nestjs/mongoose';
344
+ import { User, UserSchema } from './schemas/user.schema';
345
+
346
+ @Module({
347
+ imports: [
348
+ MongooseModule.forRoot(process.env.DATABASE_URL || 'mongodb://localhost:27017/${config.projectName}'),
349
+ MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
350
+ ],
351
+ exports: [MongooseModule],
352
+ })
353
+ export class DatabaseModule {}
354
+ `;
355
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'database.module.ts'), dbContent);
356
+ const schemasPath = path_1.default.join(srcPath, 'schemas');
357
+ await fs_extra_1.default.ensureDir(schemasPath);
358
+ const userSchema = `import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
359
+ import { Document } from 'mongoose';
360
+
361
+ export type UserDocument = User & Document;
362
+
363
+ @Schema()
364
+ export class User {
365
+ @Prop({ required: true, unique: true })
366
+ email: string;
367
+
368
+ @Prop()
369
+ name: string;
370
+
371
+ @Prop({ default: Date.now })
372
+ createdAt: Date;
373
+ }
374
+
375
+ export const UserSchema = SchemaFactory.createForClass(User);
376
+ `;
377
+ await fs_extra_1.default.writeFile(path_1.default.join(schemasPath, 'user.schema.ts'), userSchema);
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
+ }
463
+ async function generateBackendDeployment(backendPath, config) {
464
+ // Render configuration
465
+ const renderYaml = `services:
466
+ - type: web
467
+ name: ${config.projectName}-backend
468
+ env: node
469
+ buildCommand: npm install && npm run build
470
+ startCommand: npm run start:prod
471
+ envVars:
472
+ - key: NODE_ENV
473
+ value: production
474
+ - key: PORT
475
+ value: 3001
476
+ `;
477
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'render.yaml'), renderYaml);
478
+ }