speedrun-cli 2.6.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.
Files changed (159) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +620 -0
  4. package/bin/cli.js +224 -0
  5. package/index.js +12 -0
  6. package/package.json +74 -0
  7. package/src/constants.js +65 -0
  8. package/src/generator.js +271 -0
  9. package/src/index.js +13 -0
  10. package/src/moduleGenerator.js +586 -0
  11. package/src/postSetup.js +365 -0
  12. package/src/prompts.js +189 -0
  13. package/src/utils.js +112 -0
  14. package/templates/README.md +81 -0
  15. package/templates/base/eslint.config.mjs +34 -0
  16. package/templates/base/gitignore +78 -0
  17. package/templates/base/nest-cli.json +8 -0
  18. package/templates/base/src/common/constants/cookie.config.ts +20 -0
  19. package/templates/base/src/common/decorators/get-user.decorator.ts +13 -0
  20. package/templates/base/src/common/decorators/public.decorator.ts +4 -0
  21. package/templates/base/src/common/decorators/roles.decorator.ts +4 -0
  22. package/templates/base/src/common/dtos/pagination.dto.ts +29 -0
  23. package/templates/base/src/common/filters/http-exception.filter.ts +108 -0
  24. package/templates/base/src/common/guards/auth.guard.ts +51 -0
  25. package/templates/base/src/common/guards/refresh-token.guard.ts +39 -0
  26. package/templates/base/src/common/guards/roles.guard.ts +45 -0
  27. package/templates/base/src/common/interceptors/response.interceptor.ts +55 -0
  28. package/templates/base/src/common/interfaces/api-response.interface.ts +26 -0
  29. package/templates/base/src/common/middleware/correlation-id.middleware.ts +20 -0
  30. package/templates/base/src/common/validators/password.validator.ts +37 -0
  31. package/templates/base/src/config/config.module.ts +14 -0
  32. package/templates/base/src/config/logger.config.ts +109 -0
  33. package/templates/base/src/main.ts +84 -0
  34. package/templates/base/src/modules/auth/auth.controller.ts +133 -0
  35. package/templates/base/src/modules/auth/dtos/login.dto.ts +11 -0
  36. package/templates/base/src/modules/auth/dtos/signup.dto.ts +8 -0
  37. package/templates/base/src/modules/health/health.controller.ts +20 -0
  38. package/templates/base/src/modules/health/health.module.ts +7 -0
  39. package/templates/base/src/modules/users/dtos/update-profile.dto.ts +12 -0
  40. package/templates/base/src/modules/users/dtos/update-user.dto.ts +13 -0
  41. package/templates/base/src/modules/users/users.controller.ts +65 -0
  42. package/templates/base/test/app.e2e-spec.ts +24 -0
  43. package/templates/base/test/jest-e2e.json +9 -0
  44. package/templates/base/tsconfig.build.json +4 -0
  45. package/templates/base/tsconfig.json +24 -0
  46. package/templates/base-crud/CRUD_README.md +385 -0
  47. package/templates/base-crud/src/common/base/base.controller.ts +321 -0
  48. package/templates/base-crud/src/common/base/base.service.ts +192 -0
  49. package/templates/base-crud/src/common/base/index.ts +20 -0
  50. package/templates/base-crud/src/common/base/swagger/api-response.dto.ts +82 -0
  51. package/templates/base-crud/src/common/base/swagger/paginated.dto.ts +102 -0
  52. package/templates/base-crud/src/modules/products/dto/create-product.dto.ts +44 -0
  53. package/templates/base-crud/src/modules/products/dto/product.dto.ts +38 -0
  54. package/templates/base-crud/src/modules/products/dto/update-product.dto.ts +12 -0
  55. package/templates/base-crud/src/modules/products/products.controller.ts +94 -0
  56. package/templates/base-crud-drizzle/src/modules/products/products.controller.ts +79 -0
  57. package/templates/base-crud-drizzle/src/modules/products/products.module.ts +24 -0
  58. package/templates/base-crud-drizzle/src/modules/products/products.service.ts +140 -0
  59. package/templates/base-crud-drizzle/src/modules/products/schema/products.schema.ts +39 -0
  60. package/templates/base-crud-mongoose/src/modules/products/products.controller.ts +79 -0
  61. package/templates/base-crud-mongoose/src/modules/products/products.module.ts +26 -0
  62. package/templates/base-crud-mongoose/src/modules/products/products.service.ts +133 -0
  63. package/templates/base-crud-mongoose/src/modules/products/schemas/product.schema.ts +67 -0
  64. package/templates/base-crud-prisma/src/modules/products/products.module.ts +12 -0
  65. package/templates/base-crud-prisma/src/modules/products/products.service.ts +100 -0
  66. package/templates/base-crud-typeorm/src/modules/products/entities/product.entity.ts +58 -0
  67. package/templates/base-crud-typeorm/src/modules/products/products.controller.ts +79 -0
  68. package/templates/base-crud-typeorm/src/modules/products/products.module.ts +25 -0
  69. package/templates/base-crud-typeorm/src/modules/products/products.service.ts +102 -0
  70. package/templates/database/mongodb/.env.example +22 -0
  71. package/templates/database/mysql/.env.example +24 -0
  72. package/templates/database/mysql/drizzle.config.ts +13 -0
  73. package/templates/database/mysql/package.json +5 -0
  74. package/templates/database/mysql/prisma/schema.prisma +55 -0
  75. package/templates/database/mysql/src/database/drizzle.ts +13 -0
  76. package/templates/database/mysql/src/database/schema.ts +58 -0
  77. package/templates/database/postgres/.env.example +24 -0
  78. package/templates/database/postgres/drizzle.config.ts +13 -0
  79. package/templates/database/postgres/package.json +8 -0
  80. package/templates/database/postgres/prisma/schema.prisma +55 -0
  81. package/templates/database/sqlite/.env.example +24 -0
  82. package/templates/database/sqlite/drizzle.config.ts +13 -0
  83. package/templates/database/sqlite/package.json +8 -0
  84. package/templates/database/sqlite/prisma/schema.prisma +48 -0
  85. package/templates/database/sqlite/src/database/drizzle.ts +11 -0
  86. package/templates/database/sqlite/src/database/schema.ts +52 -0
  87. package/templates/orm/drizzle/drizzle.config.ts +13 -0
  88. package/templates/orm/drizzle/package.json +90 -0
  89. package/templates/orm/drizzle/src/app.module.ts +73 -0
  90. package/templates/orm/drizzle/src/config/env.validation.ts +55 -0
  91. package/templates/orm/drizzle/src/database/database.module.ts +25 -0
  92. package/templates/orm/drizzle/src/database/drizzle.ts +13 -0
  93. package/templates/orm/drizzle/src/database/schema.ts +60 -0
  94. package/templates/orm/drizzle/src/database/seed.ts +87 -0
  95. package/templates/orm/drizzle/src/modules/auth/auth.module.ts +12 -0
  96. package/templates/orm/drizzle/src/modules/auth/auth.service.ts +298 -0
  97. package/templates/orm/drizzle/src/modules/health/health.controller.ts +34 -0
  98. package/templates/orm/drizzle/src/modules/health/health.module.ts +7 -0
  99. package/templates/orm/drizzle/src/modules/users/users.module.ts +10 -0
  100. package/templates/orm/drizzle/src/modules/users/users.service.ts +152 -0
  101. package/templates/orm/mongoose/.env.example +22 -0
  102. package/templates/orm/mongoose/package.json +86 -0
  103. package/templates/orm/mongoose/src/app.module.ts +73 -0
  104. package/templates/orm/mongoose/src/config/env.validation.ts +55 -0
  105. package/templates/orm/mongoose/src/database/database.module.ts +19 -0
  106. package/templates/orm/mongoose/src/database/seed.ts +107 -0
  107. package/templates/orm/mongoose/src/modules/auth/auth.module.ts +21 -0
  108. package/templates/orm/mongoose/src/modules/auth/auth.service.ts +272 -0
  109. package/templates/orm/mongoose/src/modules/auth/dtos/login.dto.ts +10 -0
  110. package/templates/orm/mongoose/src/modules/auth/dtos/signup.dto.ts +8 -0
  111. package/templates/orm/mongoose/src/modules/health/health.controller.ts +26 -0
  112. package/templates/orm/mongoose/src/modules/health/health.module.ts +7 -0
  113. package/templates/orm/mongoose/src/modules/users/dtos/update-profile.dto.ts +24 -0
  114. package/templates/orm/mongoose/src/modules/users/dtos/update-user.dto.ts +13 -0
  115. package/templates/orm/mongoose/src/modules/users/users.module.ts +19 -0
  116. package/templates/orm/mongoose/src/modules/users/users.service.ts +179 -0
  117. package/templates/orm/mongoose/src/schemas/index.ts +2 -0
  118. package/templates/orm/mongoose/src/schemas/refresh-token.schema.ts +55 -0
  119. package/templates/orm/mongoose/src/schemas/user.schema.ts +53 -0
  120. package/templates/orm/prisma/package.json +102 -0
  121. package/templates/orm/prisma/prisma/schema.prisma +60 -0
  122. package/templates/orm/prisma/prisma/seed.ts +69 -0
  123. package/templates/orm/prisma/src/app.module.ts +55 -0
  124. package/templates/orm/prisma/src/config/env.validation.ts +56 -0
  125. package/templates/orm/prisma/src/modules/auth/auth.module.ts +17 -0
  126. package/templates/orm/prisma/src/modules/auth/auth.service.ts +308 -0
  127. package/templates/orm/prisma/src/modules/health/health.controller.ts +26 -0
  128. package/templates/orm/prisma/src/modules/health/health.module.ts +10 -0
  129. package/templates/orm/prisma/src/modules/users/users.module.ts +11 -0
  130. package/templates/orm/prisma/src/modules/users/users.service.ts +105 -0
  131. package/templates/orm/prisma/src/prisma/prisma.module.ts +9 -0
  132. package/templates/orm/prisma/src/prisma/prisma.service.ts +16 -0
  133. package/templates/orm/typeorm/package.json +100 -0
  134. package/templates/orm/typeorm/src/app.module.ts +55 -0
  135. package/templates/orm/typeorm/src/config/env.validation.ts +58 -0
  136. package/templates/orm/typeorm/src/database/data-source.ts +19 -0
  137. package/templates/orm/typeorm/src/database/database.module.ts +27 -0
  138. package/templates/orm/typeorm/src/database/seed.ts +76 -0
  139. package/templates/orm/typeorm/src/entities/index.ts +2 -0
  140. package/templates/orm/typeorm/src/entities/refresh-token.entity.ts +45 -0
  141. package/templates/orm/typeorm/src/entities/user.entity.ts +51 -0
  142. package/templates/orm/typeorm/src/modules/auth/auth.module.ts +19 -0
  143. package/templates/orm/typeorm/src/modules/auth/auth.service.ts +286 -0
  144. package/templates/orm/typeorm/src/modules/health/health.controller.ts +24 -0
  145. package/templates/orm/typeorm/src/modules/health/health.module.ts +9 -0
  146. package/templates/orm/typeorm/src/modules/users/users.module.ts +13 -0
  147. package/templates/orm/typeorm/src/modules/users/users.service.ts +108 -0
  148. package/templates/swagger/src/common/dtos/pagination.dto.ts +43 -0
  149. package/templates/swagger/src/main.ts +116 -0
  150. package/templates/swagger/src/modules/auth/auth.controller.ts +235 -0
  151. package/templates/swagger/src/modules/auth/dtos/login.dto.ts +20 -0
  152. package/templates/swagger/src/modules/auth/dtos/signup.dto.ts +14 -0
  153. package/templates/swagger/src/modules/users/dtos/update-profile.dto.ts +19 -0
  154. package/templates/swagger/src/modules/users/dtos/update-user.dto.ts +23 -0
  155. package/templates/swagger/src/modules/users/users.controller.ts +214 -0
  156. package/templates/swagger-mongoose/src/modules/auth/dtos/login.dto.ts +20 -0
  157. package/templates/swagger-mongoose/src/modules/auth/dtos/signup.dto.ts +14 -0
  158. package/templates/swagger-mongoose/src/modules/users/dtos/update-profile.dto.ts +40 -0
  159. package/templates/swagger-mongoose/src/modules/users/dtos/update-user.dto.ts +23 -0
package/bin/cli.js ADDED
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-nestjs-auth CLI
4
+ * Scaffold production-ready NestJS authentication projects
5
+ *
6
+ * @author Sabin Shrestha <sabin.shrestha.er@gmail.com>
7
+ * @license MIT
8
+ */
9
+
10
+ const { program } = require('commander');
11
+ const fs = require('fs-extra');
12
+ const path = require('path');
13
+ const { execSync } = require('child_process');
14
+ const chalk = require('chalk');
15
+
16
+ // Resolve paths relative to the package root
17
+ const packageRoot = path.join(__dirname, '..');
18
+
19
+ const {
20
+ CLI_VERSION,
21
+ ORM_OPTIONS,
22
+ DATABASE_OPTIONS,
23
+ validateAppName,
24
+ checkNodeVersion,
25
+ getInstallCommand,
26
+ promptForProjectDetails,
27
+ generateProject,
28
+ handlePostSetup,
29
+ printManualInstructions,
30
+ generateModule,
31
+ } = require(path.join(packageRoot, 'src'));
32
+
33
+ program
34
+ .name('speedrun-cli')
35
+ .version(CLI_VERSION)
36
+ .description('Create a production-ready NestJS authentication system with your choice of ORM and database');
37
+
38
+ // ==================== GENERATE COMMAND ====================
39
+ program
40
+ .command('generate [module-name]')
41
+ .alias('g')
42
+ .description('Generate a new CRUD module')
43
+ .action(async (moduleName) => {
44
+ try {
45
+ console.log(chalk.cyan(`\n⚡️ create-nestjs-auth v${CLI_VERSION} Module Generator\n`));
46
+ await generateModule(moduleName, process.cwd());
47
+ } catch (error) {
48
+ console.error(chalk.red('\n❌ Module generation failed:'));
49
+ console.error(error);
50
+ }
51
+ });
52
+
53
+ // ==================== MAIN SCAFFOLD COMMAND (DEFAULT) ====================
54
+ program
55
+ .command('create [app-name]', { isDefault: true })
56
+ .description('Create a new NestJS Auth project')
57
+ .option('--skip-install', 'Skip automatic dependency installation')
58
+ .option('--package-manager <pm>', 'Package manager to use (npm|pnpm|yarn|bun)')
59
+ .option('--skip-git', 'Skip git repository initialization')
60
+ .option('--orm <orm>', 'ORM to use (prisma|typeorm|drizzle|mongoose)')
61
+ .option('--database <db>', 'Database to use (postgres|mysql|sqlite|mongodb)')
62
+ .option('--swagger', 'Add Swagger API documentation')
63
+ .option('--base-crud', 'Generate Base CRUD Architecture (BaseService, BaseController & Swagger helpers in src/common/base)')
64
+ .option('--yes', 'Skip all prompts and use defaults')
65
+ .action(async (appName, options) => {
66
+ try {
67
+ console.log(chalk.cyan(`\n⚡️ create-nestjs-auth v${CLI_VERSION}\n`));
68
+ console.log(chalk.gray('Production-ready NestJS authentication - Now with ORM & Database choices!\n'));
69
+
70
+ // Check Node.js version
71
+ checkNodeVersion();
72
+
73
+ // Interactive mode - prompt for missing information
74
+ const projectOptions = await promptForProjectDetails(appName, options);
75
+ appName = projectOptions.appName;
76
+
77
+ // Validate app name
78
+ validateAppName(appName);
79
+
80
+ const targetDir = path.join(process.cwd(), appName);
81
+
82
+ // Check if directory exists
83
+ if (await fs.pathExists(targetDir)) {
84
+ console.error(chalk.red(`❌ Directory "${appName}" already exists`));
85
+ console.error(chalk.yellow(' Please choose a different name or remove the existing directory'));
86
+ process.exit(1);
87
+ }
88
+
89
+ console.log(chalk.blue(`\n🚀 Creating ${chalk.bold(appName)}...`));
90
+ console.log(chalk.gray(` ORM: ${ORM_OPTIONS[projectOptions.orm]?.name || projectOptions.orm}`));
91
+ console.log(chalk.gray(` Database: ${DATABASE_OPTIONS[projectOptions.database]?.name || projectOptions.database}`));
92
+ if (projectOptions.swagger) {
93
+ console.log(chalk.gray(` Swagger: ${chalk.green('Enabled')}`));
94
+ }
95
+ if (projectOptions.baseCrud) {
96
+ console.log(chalk.gray(` Base CRUD: ${chalk.green('Enabled')}`));
97
+ }
98
+ console.log('');
99
+
100
+ // Generate the project
101
+ await generateProject(targetDir, projectOptions);
102
+
103
+ // Update package.json
104
+ console.log(chalk.gray(' Updating package.json...'));
105
+ const packageJsonPath = path.join(targetDir, 'package.json');
106
+
107
+ if (await fs.pathExists(packageJsonPath)) {
108
+ const packageJson = await fs.readJSON(packageJsonPath);
109
+ packageJson.name = appName;
110
+ packageJson.version = '0.0.1';
111
+ delete packageJson.private;
112
+ await fs.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
113
+ }
114
+
115
+ // Create .env from .env.example
116
+ console.log(chalk.gray(' Setting up environment variables...'));
117
+ const envExamplePath = path.join(targetDir, '.env.example');
118
+ const envPath = path.join(targetDir, '.env');
119
+
120
+ if (await fs.pathExists(envExamplePath)) {
121
+ const envExample = await fs.readFile(envExamplePath, 'utf8');
122
+ await fs.writeFile(envPath, envExample);
123
+ }
124
+
125
+ // Install dependencies
126
+ if (projectOptions.installDependencies) {
127
+ const pm = projectOptions.packageManager;
128
+ const installCmd = getInstallCommand(pm);
129
+
130
+ console.log(chalk.yellow(`\n📦 Installing dependencies with ${chalk.bold(pm)}...`));
131
+ console.log(chalk.gray(` Running: ${installCmd}\n`));
132
+
133
+ try {
134
+ execSync(installCmd, {
135
+ cwd: targetDir,
136
+ stdio: 'inherit',
137
+ timeout: 300000,
138
+ });
139
+ } catch (error) {
140
+ console.error(chalk.red('\n❌ Dependency installation failed'));
141
+ console.error(chalk.yellow(' You can try installing manually:'));
142
+ console.error(chalk.cyan(` cd ${appName}`));
143
+ console.error(chalk.cyan(` ${installCmd}`));
144
+ process.exit(1);
145
+ }
146
+ } else {
147
+ console.log(chalk.gray('\n Skipping dependency installation (--skip-install)'));
148
+ }
149
+
150
+ // Initialize git repository
151
+ if (projectOptions.initializeGit) {
152
+ console.log(chalk.yellow('\n🔧 Initializing git repository...'));
153
+ try {
154
+ execSync('git init', { cwd: targetDir, stdio: 'ignore' });
155
+ execSync('git add -A', { cwd: targetDir, stdio: 'ignore' });
156
+ execSync('git commit -m "Initial commit from create-nestjs-auth"', {
157
+ cwd: targetDir,
158
+ stdio: 'ignore',
159
+ });
160
+ console.log(chalk.gray(' Git repository initialized with initial commit'));
161
+ } catch {
162
+ console.warn(chalk.yellow(' ⚠️ Git initialization failed (git may not be installed)'));
163
+ }
164
+ } else {
165
+ console.log(chalk.gray('\n Skipping git initialization (--skip-git)'));
166
+ }
167
+
168
+ // Post-setup interactive prompts
169
+ const completedInteractiveSetup = await handlePostSetup(
170
+ targetDir,
171
+ appName,
172
+ {
173
+ ...projectOptions,
174
+ skipInstall: !projectOptions.installDependencies,
175
+ yes: options.yes,
176
+ }
177
+ );
178
+
179
+ // Show manual instructions if interactive setup was skipped
180
+ if (!completedInteractiveSetup) {
181
+ printManualInstructions(appName, projectOptions);
182
+ } else {
183
+ console.log(chalk.white('\n📖 Documentation: https://github.com/masabinhok/create-nestjs-auth'));
184
+ console.log(chalk.white('🐛 Issues: https://github.com/masabinhok/create-nestjs-auth/issues'));
185
+ if (projectOptions.swagger) {
186
+ console.log(chalk.cyan('📄 Swagger docs: http://localhost:8080/api/docs'));
187
+ }
188
+ if (projectOptions.baseCrud) {
189
+ console.log(chalk.cyan('🏗️ Base CRUD: src/common/base/ — see CRUD_README.md'));
190
+ }
191
+ console.log(chalk.magenta('\nHappy coding! 🎉\n'));
192
+
193
+ // Post-setup module generation hook
194
+ if (projectOptions.baseCrud) {
195
+ const inquirer = require('inquirer');
196
+ const { generateNow } = await inquirer.prompt([{
197
+ type: 'confirm',
198
+ name: 'generateNow',
199
+ message: 'Do you want to generate your first CRUD module now?',
200
+ default: true
201
+ }]);
202
+
203
+ if (generateNow) {
204
+ await generateModule(undefined, targetDir, projectOptions.orm);
205
+ }
206
+ }
207
+ }
208
+
209
+ } catch (error) {
210
+ console.error(chalk.red('\n❌ An unexpected error occurred:'));
211
+ console.error(chalk.red(` ${error.message}`));
212
+
213
+ if (error.stack && process.env.DEBUG) {
214
+ console.error(chalk.gray('\n Stack trace:'));
215
+ console.error(chalk.gray(error.stack));
216
+ }
217
+
218
+ console.error(chalk.yellow('\n If this persists, please report it:'));
219
+ console.error(chalk.cyan(' https://github.com/masabinhok/create-nestjs-auth/issues\n'));
220
+ process.exit(1);
221
+ }
222
+ });
223
+
224
+ program.parse(process.argv);
package/index.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-nestjs-auth CLI
4
+ *
5
+ * Entry point for npx/npm execution.
6
+ * All logic has been modularized into src/ directory.
7
+ *
8
+ * @see ./bin/cli.js for the main CLI implementation
9
+ * @see ./src/ for modular source code
10
+ */
11
+
12
+ require('./bin/cli');
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "speedrun-cli",
3
+ "version": "2.6.1",
4
+ "description": "CLI tool to scaffold a production-ready NestJS authentication system with JWT, refresh tokens, and RBAC",
5
+ "keywords": [
6
+ "nestjs",
7
+ "authentication",
8
+ "jwt",
9
+ "auth",
10
+ "boilerplate",
11
+ "starter",
12
+ "template",
13
+ "rbac",
14
+ "refresh-token",
15
+ "prisma",
16
+ "drizzle",
17
+ "typeorm",
18
+ "mongoose",
19
+ "postgresql",
20
+ "mysql",
21
+ "mongodb",
22
+ "sqlite",
23
+ "cli",
24
+ "scaffold",
25
+ "generator"
26
+ ],
27
+ "author": {
28
+ "name": "Sabin Shrestha",
29
+ "email": "sabin.shrestha.er@gmail.com",
30
+ "url": "https://sabinshrestha69.com.np"
31
+ },
32
+ "license": "MIT",
33
+ "homepage": "https://github.com/masabinhok/create-nestjs-auth#readme",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/masabinhok/create-nestjs-auth.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/masabinhok/create-nestjs-auth/issues"
40
+ },
41
+ "bin": {
42
+ "create-nestjs-auth": "./index.js"
43
+ },
44
+ "main": "./index.js",
45
+ "type": "commonjs",
46
+ "engines": {
47
+ "node": ">=20.0.0"
48
+ },
49
+ "scripts": {
50
+ "test": "node index.js test-output --skip-install --skip-git --yes",
51
+ "test:full": "node index.js test-output",
52
+ "test:prisma": "node index.js test-prisma --orm prisma --database postgres --skip-install --skip-git --yes",
53
+ "test:drizzle": "node index.js test-drizzle --orm drizzle --database postgres --skip-install --skip-git --yes",
54
+ "test:typeorm": "node index.js test-typeorm --orm typeorm --database postgres --skip-install --skip-git --yes",
55
+ "test:mongoose": "node index.js test-mongoose --orm mongoose --skip-install --skip-git --yes",
56
+ "clean": "node -e \"require('fs-extra').removeSync('test-output'); require('fs-extra').removeSync('test-prisma'); require('fs-extra').removeSync('test-drizzle'); require('fs-extra').removeSync('test-typeorm'); require('fs-extra').removeSync('test-mongoose');\"",
57
+ "prepublishOnly": "npm run clean && echo 'Ready to publish create-nestjs-auth'"
58
+ },
59
+ "files": [
60
+ "index.js",
61
+ "bin/",
62
+ "src/",
63
+ "templates/",
64
+ "README.md",
65
+ "LICENSE",
66
+ "CHANGELOG.md"
67
+ ],
68
+ "dependencies": {
69
+ "chalk": "^4.1.2",
70
+ "commander": "^14.0.0",
71
+ "fs-extra": "^11.2.0",
72
+ "inquirer": "^8.2.6"
73
+ }
74
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CLI Constants and Configuration
3
+ * @module constants
4
+ */
5
+
6
+ const ORM_OPTIONS = {
7
+ prisma: {
8
+ name: 'Prisma',
9
+ description: 'Next-generation ORM with type safety',
10
+ databases: ['postgres', 'mysql', 'sqlite'],
11
+ },
12
+ typeorm: {
13
+ name: 'TypeORM',
14
+ description: 'Traditional ORM with decorators',
15
+ databases: ['postgres', 'mysql', 'sqlite'],
16
+ },
17
+ drizzle: {
18
+ name: 'Drizzle',
19
+ description: 'Lightweight TypeScript ORM',
20
+ databases: ['postgres', 'mysql', 'sqlite'],
21
+ },
22
+ mongoose: {
23
+ name: 'Mongoose',
24
+ description: 'MongoDB ODM (MongoDB only)',
25
+ databases: ['mongodb'],
26
+ fixedDatabase: true,
27
+ },
28
+ };
29
+
30
+ const DATABASE_OPTIONS = {
31
+ postgres: {
32
+ name: 'PostgreSQL',
33
+ urlTemplate: 'postgresql://user:password@localhost:5432/database_name',
34
+ urlPrefix: ['postgresql://', 'postgres://'],
35
+ },
36
+ mysql: {
37
+ name: 'MySQL',
38
+ urlTemplate: 'mysql://user:password@localhost:3306/database_name',
39
+ urlPrefix: ['mysql://'],
40
+ },
41
+ sqlite: {
42
+ name: 'SQLite',
43
+ urlTemplate: 'file:./dev.db',
44
+ urlPrefix: ['file:'],
45
+ devOnly: true,
46
+ },
47
+ mongodb: {
48
+ name: 'MongoDB',
49
+ urlTemplate: 'mongodb://user:password@localhost:27017/database_name',
50
+ urlPrefix: ['mongodb://', 'mongodb+srv://'],
51
+ },
52
+ };
53
+
54
+ const RESERVED_NAMES = ['node_modules', 'favicon.ico'];
55
+
56
+ const path = require('path');
57
+ const packageJson = require(path.join(__dirname, '..', 'package.json'));
58
+ const CLI_VERSION = packageJson.version;
59
+
60
+ module.exports = {
61
+ ORM_OPTIONS,
62
+ DATABASE_OPTIONS,
63
+ RESERVED_NAMES,
64
+ CLI_VERSION,
65
+ };
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Project Generation Logic
3
+ * @module generator
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+ const { ORM_OPTIONS } = require('./constants');
10
+
11
+ /**
12
+ * Generates the project by merging base, ORM, and database templates
13
+ * @param {string} targetDir - Target directory for the project
14
+ * @param {object} options - Generation options
15
+ */
16
+ async function generateProject(targetDir, options) {
17
+ const { orm, database, swagger, baseCrud } = options;
18
+ const templatesDir = path.join(__dirname, '..', 'templates');
19
+
20
+ const baseDir = path.join(templatesDir, 'base');
21
+ const ormDir = path.join(templatesDir, 'orm', orm);
22
+ const dbDir = path.join(templatesDir, 'database', database);
23
+ const swaggerDir = path.join(templatesDir, 'swagger');
24
+ const swaggerMongooseDir = path.join(templatesDir, 'swagger-mongoose');
25
+
26
+ const useNewStructure = await fs.pathExists(baseDir);
27
+
28
+ if (useNewStructure) {
29
+ await generateFromModularTemplates(targetDir, { baseDir, ormDir, dbDir, orm, swagger, swaggerDir, swaggerMongooseDir, baseCrud });
30
+ } else {
31
+ await generateFromLegacyTemplate(targetDir);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Generates project from modular template structure
37
+ * @param {string} targetDir - Target directory
38
+ * @param {object} dirs - Directory paths
39
+ */
40
+ async function generateFromModularTemplates(targetDir, { baseDir, ormDir, dbDir, orm, swagger, swaggerDir, swaggerMongooseDir, baseCrud }) {
41
+ console.log(chalk.gray(' Using modular template structure...'));
42
+
43
+ // Ensure target directory exists
44
+ await fs.ensureDir(targetDir);
45
+
46
+ // Step 1: Copy base template
47
+ console.log(chalk.gray(' Copying base template...'));
48
+ await fs.copy(baseDir, targetDir, {
49
+ filter: createCopyFilter(baseDir),
50
+ });
51
+
52
+ // Step 2: Rename gitignore to .gitignore (npm excludes .gitignore files from packages)
53
+ const gitignoreSrc = path.join(targetDir, 'gitignore');
54
+ const gitignoreDest = path.join(targetDir, '.gitignore');
55
+ if (await fs.pathExists(gitignoreSrc)) {
56
+ await fs.rename(gitignoreSrc, gitignoreDest);
57
+ }
58
+
59
+ // Step 3: Apply ORM adapter
60
+ if (await fs.pathExists(ormDir)) {
61
+ console.log(chalk.gray(` Applying ${ORM_OPTIONS[orm]?.name || orm} adapter...`));
62
+ await fs.copy(ormDir, targetDir, {
63
+ overwrite: true,
64
+ filter: createCopyFilter(ormDir, ['package.json']),
65
+ });
66
+
67
+ await mergePackageJson(ormDir, targetDir);
68
+ }
69
+
70
+ // Step 4: Apply database configuration
71
+ if (await fs.pathExists(dbDir)) {
72
+ console.log(chalk.gray(` Configuring database...`));
73
+ await fs.copy(dbDir, targetDir, {
74
+ overwrite: true,
75
+ filter: createOrmSpecificFilter(orm, dbDir),
76
+ });
77
+
78
+ await mergePackageJson(dbDir, targetDir, true);
79
+ }
80
+
81
+ // Step 5: Apply Swagger documentation overlay
82
+ if (swagger) {
83
+ console.log(chalk.gray(' Adding Swagger documentation...'));
84
+ if (await fs.pathExists(swaggerDir)) {
85
+ await fs.copy(swaggerDir, targetDir, {
86
+ overwrite: true,
87
+ filter: createCopyFilter(swaggerDir),
88
+ });
89
+ }
90
+
91
+ // Apply ORM-specific Swagger overrides (e.g., Mongoose DTOs)
92
+ if (orm === 'mongoose' && await fs.pathExists(swaggerMongooseDir)) {
93
+ await fs.copy(swaggerMongooseDir, targetDir, {
94
+ overwrite: true,
95
+ filter: createCopyFilter(swaggerMongooseDir),
96
+ });
97
+ }
98
+
99
+ // Add Swagger dependencies to package.json
100
+ await addSwaggerDependencies(targetDir);
101
+ }
102
+
103
+ // Step 6: Apply Base CRUD Architecture overlay
104
+ if (baseCrud) {
105
+ // 6a. Shared layer: abstract base + shared DTOs + shared controller
106
+ const baseCrudDir = path.join(__dirname, '..', 'templates', 'base-crud');
107
+ if (await fs.pathExists(baseCrudDir)) {
108
+ console.log(chalk.gray(' Adding Base CRUD Architecture (abstract layer + shared DTOs)...'));
109
+ await fs.copy(baseCrudDir, targetDir, {
110
+ overwrite: true,
111
+ filter: createCopyFilter(baseCrudDir),
112
+ });
113
+ }
114
+
115
+ // 6b. ORM-specific ProductModule example (overrides products.service.ts + module)
116
+ const baseCrudOrmDir = path.join(__dirname, '..', 'templates', `base-crud-${orm}`);
117
+ if (await fs.pathExists(baseCrudOrmDir)) {
118
+ console.log(chalk.gray(` Adding ${ORM_OPTIONS[orm]?.name || orm} ProductModule example...`));
119
+ await fs.copy(baseCrudOrmDir, targetDir, {
120
+ overwrite: true,
121
+ filter: createCopyFilter(baseCrudOrmDir),
122
+ });
123
+ }
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Generates project from legacy single-template structure
129
+ * @param {string} targetDir - Target directory
130
+ */
131
+ async function generateFromLegacyTemplate(targetDir) {
132
+ console.log(chalk.gray(' Using legacy template structure...'));
133
+ const templateDir = path.join(__dirname, '..', 'template');
134
+
135
+ if (!(await fs.pathExists(templateDir))) {
136
+ console.error(chalk.red('❌ Template directory not found'));
137
+ console.error(chalk.yellow(' Please reinstall create-nestjs-auth: npm install -g create-nestjs-auth'));
138
+ process.exit(1);
139
+ }
140
+
141
+ await fs.copy(templateDir, targetDir, {
142
+ filter: (src) => {
143
+ const relativePath = path.relative(templateDir, src);
144
+ const basename = path.basename(src);
145
+ if (basename === '.gitignore') return true;
146
+ return !relativePath.startsWith('.git' + path.sep) &&
147
+ relativePath !== '.git' &&
148
+ !relativePath.includes('node_modules') &&
149
+ !relativePath.includes('dist');
150
+ },
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Creates a copy filter function
156
+ * @param {string} baseDir - The base directory being copied from
157
+ * @param {Array<string>} excludeFiles - Additional files to exclude
158
+ * @returns {function} Filter function
159
+ */
160
+ function createCopyFilter(baseDir, excludeFiles = []) {
161
+ return (src) => {
162
+ const basename = path.basename(src);
163
+ const relativePath = path.relative(baseDir, src);
164
+
165
+ // Allow gitignore (renamed from .gitignore for npm compatibility)
166
+ if (basename === 'gitignore' || basename === '.gitignore') return true;
167
+ if (excludeFiles.includes(basename)) return false;
168
+
169
+ // Check for node_modules and .git only within the template directory
170
+ return !relativePath.includes('node_modules') &&
171
+ !relativePath.includes(path.sep + '.git') &&
172
+ basename !== '.git';
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Creates ORM-specific filter for database templates
178
+ * @param {string} orm - Selected ORM
179
+ * @param {string} dbDir - Database directory path
180
+ * @returns {function} Filter function
181
+ */
182
+ function createOrmSpecificFilter(orm, dbDir) {
183
+ const drizzleFiles = ['drizzle.config.ts', 'drizzle'];
184
+ const prismaFiles = ['prisma'];
185
+
186
+ return (src) => {
187
+ const basename = path.basename(src);
188
+ const relativePath = path.relative(dbDir, src);
189
+
190
+ // Allow gitignore (renamed from .gitignore for npm compatibility)
191
+ if (basename === 'gitignore' || basename === '.gitignore') return true;
192
+ // Check for node_modules only within the template directory (not in the CLI install path)
193
+ if (relativePath.includes('node_modules') || basename === 'package.json') return false;
194
+
195
+ // Filter based on ORM
196
+ if (orm === 'prisma') {
197
+ if (drizzleFiles.some((f) => relativePath.startsWith(f) || basename === f)) return false;
198
+ if (relativePath.includes('src' + path.sep + 'database')) return false;
199
+ } else if (orm === 'drizzle') {
200
+ if (prismaFiles.some((f) => relativePath.startsWith(f) || basename === f)) return false;
201
+ } else if (orm === 'typeorm' || orm === 'mongoose') {
202
+ if (drizzleFiles.some((f) => relativePath.startsWith(f) || basename === f)) return false;
203
+ if (prismaFiles.some((f) => relativePath.startsWith(f) || basename === f)) return false;
204
+ if (relativePath.includes('src' + path.sep + 'database')) return false;
205
+ }
206
+
207
+ return true;
208
+ };
209
+ }
210
+
211
+ /**
212
+ * Merges package.json files
213
+ * @param {string} sourceDir - Source directory
214
+ * @param {string} targetDir - Target directory
215
+ * @param {boolean} dependenciesOnly - Only merge dependencies
216
+ */
217
+ async function mergePackageJson(sourceDir, targetDir, dependenciesOnly = false) {
218
+ const sourcePackageJsonPath = path.join(sourceDir, 'package.json');
219
+ const targetPackageJsonPath = path.join(targetDir, 'package.json');
220
+
221
+ if (!(await fs.pathExists(sourcePackageJsonPath))) return;
222
+
223
+ const sourcePackageJson = await fs.readJSON(sourcePackageJsonPath);
224
+ let targetPackageJson = {};
225
+
226
+ // Ensure target directory exists before writing
227
+ await fs.ensureDir(targetDir);
228
+
229
+ if (await fs.pathExists(targetPackageJsonPath)) {
230
+ targetPackageJson = await fs.readJSON(targetPackageJsonPath);
231
+ }
232
+
233
+ if (dependenciesOnly) {
234
+ targetPackageJson = {
235
+ ...targetPackageJson,
236
+ dependencies: { ...targetPackageJson.dependencies, ...sourcePackageJson.dependencies },
237
+ devDependencies: { ...targetPackageJson.devDependencies, ...sourcePackageJson.devDependencies },
238
+ };
239
+ } else {
240
+ targetPackageJson = {
241
+ ...targetPackageJson,
242
+ ...sourcePackageJson,
243
+ dependencies: { ...targetPackageJson.dependencies, ...sourcePackageJson.dependencies },
244
+ devDependencies: { ...targetPackageJson.devDependencies, ...sourcePackageJson.devDependencies },
245
+ scripts: { ...targetPackageJson.scripts, ...sourcePackageJson.scripts },
246
+ };
247
+ }
248
+
249
+ await fs.writeJSON(targetPackageJsonPath, targetPackageJson, { spaces: 2 });
250
+ }
251
+
252
+ /**
253
+ * Adds Swagger dependencies to the project's package.json
254
+ * @param {string} targetDir - Target directory
255
+ */
256
+ async function addSwaggerDependencies(targetDir) {
257
+ const packageJsonPath = path.join(targetDir, 'package.json');
258
+ if (!(await fs.pathExists(packageJsonPath))) return;
259
+
260
+ const packageJson = await fs.readJSON(packageJsonPath);
261
+ packageJson.dependencies = {
262
+ ...packageJson.dependencies,
263
+ '@nestjs/swagger': '^11.0.0',
264
+ 'swagger-ui-express': '^5.0.1',
265
+ };
266
+ await fs.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
267
+ }
268
+
269
+ module.exports = {
270
+ generateProject,
271
+ };
package/src/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Module exports for CLI source files
3
+ * @module src
4
+ */
5
+
6
+ module.exports = {
7
+ ...require('./constants'),
8
+ ...require('./utils'),
9
+ ...require('./prompts'),
10
+ ...require('./generator'),
11
+ ...require('./postSetup'),
12
+ ...require('./moduleGenerator'),
13
+ };