speedrun-cli 2.6.6 → 2.6.8

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/bin/cli.js CHANGED
@@ -189,21 +189,6 @@ program
189
189
  console.log(chalk.cyan('🏗️ Base CRUD: src/common/base/ — see CRUD_README.md'));
190
190
  }
191
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
192
  }
208
193
 
209
194
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speedrun-cli",
3
- "version": "2.6.6",
3
+ "version": "2.6.8",
4
4
  "description": "CLI tool to scaffold a production-ready NestJS authentication system with JWT, refresh tokens, and RBAC",
5
5
  "keywords": [
6
6
  "nestjs",
@@ -141,7 +141,7 @@ class TypeOrm${pascalName}Repository implements IBaseRepository<${pascalName}Ent
141
141
  export class ${pascalName}Service extends BaseService<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
142
142
  private readonly repository: TypeOrm${pascalName}Repository;
143
143
 
144
- constructor(@InjectRepository(Object) private readonly repo: Repository<${pascalName}Entity>) {
144
+ constructor(@InjectRepository(Object /* Replace with your Entity class, e.g. ${pascalName}Entity */) private readonly repo: Repository<${pascalName}Entity>) {
145
145
  super();
146
146
  this.repository = new TypeOrm${pascalName}Repository(this.repo);
147
147
  }
@@ -259,7 +259,7 @@ class Drizzle${pascalName}Repository implements IBaseRepository<${pascalName}Ent
259
259
  export class ${pascalName}Service extends BaseService<${pascalName}Entity, ${createDtoName}, ${updateDtoName}> {
260
260
  private readonly repository: Drizzle${pascalName}Repository;
261
261
 
262
- constructor(@Inject('DB_CONNECTION') private readonly db: any) {
262
+ constructor(@Inject('DRIZZLE') private readonly db: any) {
263
263
  super();
264
264
  this.repository = new Drizzle${pascalName}Repository(this.db);
265
265
  }
@@ -395,26 +395,23 @@ async function registerInAppModule(targetDir, pascalName, kebabName) {
395
395
  }
396
396
 
397
397
  let content = await fs.readFile(appModulePath, 'utf8');
398
-
399
398
  const moduleImport = `import { ${pascalName}Module } from './modules/${kebabName}/${kebabName}.module';`;
400
399
 
401
- // Skip if already imported
402
- if (content.includes(moduleImport) || content.includes(`${pascalName}Module`)) {
400
+ // Prevent duplicate registration — use exact module name match
401
+ if (content.includes(moduleImport) || new RegExp(`\\b${pascalName}Module\\b`).test(content)) {
403
402
  return true;
404
403
  }
405
404
 
406
- // 1. Add import statement at top
405
+ // 1. Tambahkan baris import di paling atas file
407
406
  content = `${moduleImport}\n` + content;
408
407
 
409
- // 2. Inject ${pascalName}Module into imports array
410
- const importsArrayRegex = /(imports\s*:\s*\[)([^\]]*)/s;
411
- if (importsArrayRegex.test(content)) {
412
- content = content.replace(importsArrayRegex, (match, p1, p2) => {
413
- const trimmedP2 = p2.trim();
414
- const separator = trimmedP2 ? (trimmedP2.endsWith(',') ? '\n ' : ',\n ') : '\n ';
415
- return `${p1}${p2}${separator}${pascalName}Module,`;
416
- });
417
-
408
+ // 2. Inject ${pascalName}Module tepat di dalam decorator @Module({ imports: [ ... ] })
409
+ const importsKeywordIndex = content.indexOf('imports: [');
410
+
411
+ if (importsKeywordIndex !== -1) {
412
+ const insertPosition = importsKeywordIndex + 'imports: ['.length;
413
+ content = content.slice(0, insertPosition) + `\n ${pascalName}Module,` + content.slice(insertPosition);
414
+
418
415
  await fs.writeFile(appModulePath, content, 'utf8');
419
416
  console.log(chalk.green(`✨ Automatically registered ${pascalName}Module in src/app.module.ts`));
420
417
  return true;
package/src/postSetup.js CHANGED
@@ -10,6 +10,12 @@ const chalk = require('chalk');
10
10
  const inquirer = require('inquirer');
11
11
  const { ORM_OPTIONS, DATABASE_OPTIONS } = require('./constants');
12
12
  const { generateJWTSecret, getRunPrefix } = require('./utils');
13
+ // Lazy-loaded to avoid circular deps: moduleGenerator requires constants/utils
14
+ let _generateModule;
15
+ function getGenerateModule() {
16
+ if (!_generateModule) _generateModule = require('./moduleGenerator').generateModule;
17
+ return _generateModule;
18
+ }
13
19
 
14
20
  /**
15
21
  * Handles interactive post-setup configuration
@@ -30,7 +36,7 @@ async function handlePostSetup(targetDir, appName, options) {
30
36
  const { continueSetup } = await inquirer.prompt([{
31
37
  type: 'confirm',
32
38
  name: 'continueSetup',
33
- message: 'Would you like to complete the setup now? (JWT secrets, database, etc.)',
39
+ message: 'Would you like to complete the setup now? (JWT secrets, database, CRUD)',
34
40
  default: true,
35
41
  }]);
36
42
 
@@ -38,13 +44,16 @@ async function handlePostSetup(targetDir, appName, options) {
38
44
  return false;
39
45
  }
40
46
 
41
- // Configure JWT secrets and database URL
47
+ // Step 1: Configure JWT secrets and database URL
42
48
  await configureEnvironment(targetDir, database);
43
49
 
44
- // ORM-specific database setup
50
+ // Step 2: ORM-specific database setup (Schema/Migration → Seed)
45
51
  await setupDatabase(targetDir, orm, packageManager);
46
52
 
47
- // Optionally start dev server
53
+ // Step 3: CRUD module generation (always offered when setup is accepted)
54
+ await promptCrudGeneration(targetDir, orm);
55
+
56
+ // Step 4: Optionally start dev server
48
57
  await promptDevServer(targetDir, packageManager);
49
58
 
50
59
  return true;
@@ -272,6 +281,30 @@ function printCredentials() {
272
281
  console.log(chalk.white(' Password: Admin@123\n'));
273
282
  }
274
283
 
284
+ /**
285
+ * Prompts user to generate their first CRUD module
286
+ */
287
+ async function promptCrudGeneration(targetDir, orm) {
288
+ const { generateNow } = await inquirer.prompt([{
289
+ type: 'confirm',
290
+ name: 'generateNow',
291
+ message: 'Do you want to generate your first CRUD module now?',
292
+ default: true,
293
+ }]);
294
+
295
+ if (!generateNow) {
296
+ console.log(chalk.gray('\n Skipping CRUD generation. Run `speedrun-cli generate <name>` anytime.\n'));
297
+ return;
298
+ }
299
+
300
+ try {
301
+ await getGenerateModule()(undefined, targetDir, orm);
302
+ } catch (err) {
303
+ console.warn(chalk.yellow(`\n ⚠️ CRUD generation failed: ${err.message}`));
304
+ console.warn(chalk.gray(' Run `speedrun-cli generate <name>` manually inside your project.\n'));
305
+ }
306
+ }
307
+
275
308
  /**
276
309
  * Prompts user to start dev server
277
310
  */
@@ -339,6 +372,10 @@ function printManualInstructions(appName, options) {
339
372
  commands.forEach((cmd) => console.log(chalk.gray(` npm run ${cmd}`)));
340
373
  }
341
374
 
375
+ console.log(chalk.cyan('\n # Generate your first CRUD module:'));
376
+ console.log(chalk.gray(' speedrun-cli generate <module-name>'));
377
+ console.log(chalk.gray(' # e.g. speedrun-cli generate orders'));
378
+
342
379
  console.log(chalk.cyan('\n # Start development server:'));
343
380
  console.log(chalk.gray(' npm run start:dev'));
344
381