add-nest-auth 1.0.6 → 1.0.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/README.md CHANGED
@@ -359,9 +359,9 @@ If this tool helped you, please consider:
359
359
 
360
360
  ## 🔗 Links
361
361
 
362
- - **GitHub**: https://github.com/yourusername/add-nest-auth
362
+ - **GitHub**: https://github.com/Islamawad132/add-nest-auth
363
363
  - **npm**: https://www.npmjs.com/package/add-nest-auth
364
- - **Issues**: https://github.com/yourusername/add-nest-auth/issues
364
+ - **Issues**: https://github.com/Islamawad132/add-nest-auth/issues
365
365
 
366
366
  ---
367
367
 
package/dist/cli.js CHANGED
@@ -127,6 +127,7 @@ var init_file_writer = __esm({
127
127
  fs3 = __toESM(require("fs-extra"));
128
128
  FileWriter = class {
129
129
  writtenFiles = [];
130
+ skippedFiles = [];
130
131
  backups = /* @__PURE__ */ new Map();
131
132
  /**
132
133
  * Write a file to disk
@@ -135,7 +136,8 @@ var init_file_writer = __esm({
135
136
  const { overwrite = false, backup = true } = options;
136
137
  const exists = await fs3.pathExists(filePath);
137
138
  if (exists && !overwrite) {
138
- throw new Error(`File already exists: ${filePath}`);
139
+ this.skippedFiles.push(filePath);
140
+ return;
139
141
  }
140
142
  if (exists && backup) {
141
143
  await this.createBackup(filePath);
@@ -187,6 +189,12 @@ var init_file_writer = __esm({
187
189
  getWrittenFiles() {
188
190
  return [...this.writtenFiles];
189
191
  }
192
+ /**
193
+ * Get list of skipped files (already existed)
194
+ */
195
+ getSkippedFiles() {
196
+ return [...this.skippedFiles];
197
+ }
190
198
  };
191
199
  }
192
200
  });
@@ -225,7 +233,7 @@ var init_generator = __esm({
225
233
  /**
226
234
  * Generate all authentication files
227
235
  */
228
- async generate(config, projectInfo) {
236
+ async generate(config, projectInfo, overwrite = false) {
229
237
  try {
230
238
  const context = buildTemplateContext(config);
231
239
  const plan = this.buildGenerationPlan(config);
@@ -239,19 +247,22 @@ var init_generator = __esm({
239
247
  );
240
248
  const outputPath = path4.join(projectInfo.root, fileSpec.output);
241
249
  await this.fileWriter.writeFile(outputPath, content, {
242
- overwrite: false
250
+ overwrite
243
251
  });
244
252
  }
245
253
  const filesCreated = this.fileWriter.getWrittenFiles();
254
+ const filesSkipped = this.fileWriter.getSkippedFiles();
246
255
  await this.fileWriter.cleanupBackups();
247
256
  return {
248
257
  filesCreated,
258
+ filesSkipped,
249
259
  success: true
250
260
  };
251
261
  } catch (error) {
252
262
  await this.fileWriter.rollback();
253
263
  return {
254
264
  filesCreated: [],
265
+ filesSkipped: [],
255
266
  success: false,
256
267
  error: error instanceof Error ? error.message : "Unknown error"
257
268
  };
@@ -348,7 +359,10 @@ var init_ast_updater = __esm({
348
359
  constructor(appModulePath) {
349
360
  this.appModulePath = appModulePath;
350
361
  this.project = new import_ts_morph.Project({
351
- skipAddingFilesFromTsConfig: true
362
+ skipAddingFilesFromTsConfig: true,
363
+ manipulationSettings: {
364
+ indentationText: import_ts_morph.IndentationText.TwoSpaces
365
+ }
352
366
  });
353
367
  }
354
368
  project;
@@ -357,12 +371,13 @@ var init_ast_updater = __esm({
357
371
  /**
358
372
  * Update app.module.ts with auth modules
359
373
  */
360
- async update() {
374
+ async update(config) {
361
375
  await this.createBackup();
362
376
  try {
363
377
  this.sourceFile = this.project.addSourceFileAtPath(this.appModulePath);
364
- this.addImports();
365
- this.addModulesToDecorator();
378
+ this.addImports(config);
379
+ this.addModulesToDecorator(config);
380
+ this.sourceFile.formatText();
366
381
  await this.sourceFile.save();
367
382
  } catch (error) {
368
383
  await this.restoreBackup();
@@ -372,11 +387,18 @@ var init_ast_updater = __esm({
372
387
  /**
373
388
  * Add necessary imports
374
389
  */
375
- addImports() {
390
+ addImports(config) {
376
391
  if (!this.sourceFile) {
377
392
  throw new Error("Source file not loaded");
378
393
  }
379
394
  this.addImport("@nestjs/config", ["ConfigModule"]);
395
+ if (config && config.orm === "typeorm") {
396
+ this.addImport("@nestjs/typeorm", ["TypeOrmModule"]);
397
+ this.addImport("./users/entities/user.entity", ["User"]);
398
+ if (config.features.refreshTokens) {
399
+ this.addImport("./users/entities/refresh-token.entity", ["RefreshToken"]);
400
+ }
401
+ }
380
402
  this.addImport("./auth/auth.module", ["AuthModule"]);
381
403
  this.addImport("./users/users.module", ["UsersModule"]);
382
404
  }
@@ -404,7 +426,7 @@ var init_ast_updater = __esm({
404
426
  /**
405
427
  * Add modules to @Module decorator imports array
406
428
  */
407
- addModulesToDecorator() {
429
+ addModulesToDecorator(config) {
408
430
  if (!this.sourceFile) return;
409
431
  const appModuleClass = this.sourceFile.getClass("AppModule");
410
432
  if (!appModuleClass) {
@@ -437,6 +459,11 @@ var init_ast_updater = __esm({
437
459
  if (!existingModules.has("ConfigModule")) {
438
460
  importsArray.addElement("ConfigModule.forRoot({ isGlobal: true })");
439
461
  }
462
+ if (config && config.orm === "typeorm" && !existingModules.has("TypeOrmModule")) {
463
+ const entities = config.features.refreshTokens ? "[User, RefreshToken]" : "[User]";
464
+ const typeOrmConfig = this.buildTypeOrmConfig(config.database, entities);
465
+ importsArray.addElement(typeOrmConfig);
466
+ }
440
467
  if (!existingModules.has("AuthModule")) {
441
468
  importsArray.addElement("AuthModule");
442
469
  }
@@ -444,6 +471,43 @@ var init_ast_updater = __esm({
444
471
  importsArray.addElement("UsersModule");
445
472
  }
446
473
  }
474
+ /**
475
+ * Build TypeORM.forRoot() configuration string based on database type
476
+ */
477
+ buildTypeOrmConfig(database, entities) {
478
+ switch (database) {
479
+ case "sqlite":
480
+ return `TypeOrmModule.forRoot({
481
+ type: 'sqlite',
482
+ database: 'database.sqlite',
483
+ entities: ${entities},
484
+ synchronize: true, // WARNING: disable in production!
485
+ })`;
486
+ case "mysql":
487
+ return `TypeOrmModule.forRoot({
488
+ type: 'mysql',
489
+ host: process.env.DATABASE_HOST || 'localhost',
490
+ port: parseInt(process.env.DATABASE_PORT || '3306'),
491
+ username: process.env.DATABASE_USER || 'root',
492
+ password: process.env.DATABASE_PASSWORD || '',
493
+ database: process.env.DATABASE_NAME || 'auth_db',
494
+ entities: ${entities},
495
+ synchronize: true, // WARNING: disable in production!
496
+ })`;
497
+ case "postgres":
498
+ default:
499
+ return `TypeOrmModule.forRoot({
500
+ type: 'postgres',
501
+ host: process.env.DATABASE_HOST || 'localhost',
502
+ port: parseInt(process.env.DATABASE_PORT || '5432'),
503
+ username: process.env.DATABASE_USER || 'postgres',
504
+ password: process.env.DATABASE_PASSWORD || 'postgres',
505
+ database: process.env.DATABASE_NAME || 'auth_db',
506
+ entities: ${entities},
507
+ synchronize: true, // WARNING: disable in production!
508
+ })`;
509
+ }
510
+ }
447
511
  /**
448
512
  * Get existing module names from imports array
449
513
  */
@@ -501,7 +565,10 @@ var init_main_ts_updater = __esm({
501
565
  constructor(mainTsPath) {
502
566
  this.mainTsPath = mainTsPath;
503
567
  this.project = new import_ts_morph2.Project({
504
- skipAddingFilesFromTsConfig: true
568
+ skipAddingFilesFromTsConfig: true,
569
+ manipulationSettings: {
570
+ indentationText: import_ts_morph2.IndentationText.TwoSpaces
571
+ }
505
572
  });
506
573
  }
507
574
  project;
@@ -519,6 +586,7 @@ var init_main_ts_updater = __esm({
519
586
  this.sourceFile = this.project.addSourceFileAtPath(this.mainTsPath);
520
587
  this.addImports();
521
588
  this.addGlobalGuardsAndPipes();
589
+ this.sourceFile.formatText();
522
590
  await this.sourceFile.save();
523
591
  } catch (error) {
524
592
  await this.restoreBackup();
@@ -588,19 +656,19 @@ var init_main_ts_updater = __esm({
588
656
  }
589
657
  const codeToInsert = [
590
658
  "",
591
- " // Enable global validation pipe",
592
- " app.useGlobalPipes(",
593
- " new ValidationPipe({",
594
- " whitelist: true,",
595
- " forbidNonWhitelisted: true,",
596
- " transform: true,",
597
- " }),",
598
- " );",
659
+ "// Enable global validation pipe",
660
+ "app.useGlobalPipes(",
661
+ " new ValidationPipe({",
662
+ " whitelist: true,",
663
+ " forbidNonWhitelisted: true,",
664
+ " transform: true,",
665
+ " }),",
666
+ ");",
599
667
  "",
600
- " // Enable global JWT guard (all routes protected by default)",
601
- " // Use @Public() decorator on routes that should be accessible without auth",
602
- " const reflector = app.get(Reflector);",
603
- " app.useGlobalGuards(new JwtAuthGuard(reflector));",
668
+ "// Enable global JWT guard (all routes protected by default)",
669
+ "// Use @Public() decorator on routes that should be accessible without auth",
670
+ "const reflector = app.get(Reflector);",
671
+ "app.useGlobalGuards(new JwtAuthGuard(reflector));",
604
672
  ""
605
673
  ].join("\n");
606
674
  body.insertStatements(listenIndex, codeToInsert);
@@ -840,6 +908,25 @@ async function detectORM(packageJson) {
840
908
  }
841
909
  return "none";
842
910
  }
911
+ function detectDatabase(packageJson, orm) {
912
+ const dependencies = {
913
+ ...packageJson.dependencies,
914
+ ...packageJson.devDependencies
915
+ };
916
+ if (orm === "typeorm") {
917
+ if (dependencies["pg"]) return "postgres";
918
+ if (dependencies["mysql2"] || dependencies["mysql"]) return "mysql";
919
+ if (dependencies["sqlite3"]) return "sqlite";
920
+ if (dependencies["mongodb"]) return "mongodb";
921
+ }
922
+ if (orm === "prisma") {
923
+ return void 0;
924
+ }
925
+ if (orm === "mongoose") {
926
+ return "mongodb";
927
+ }
928
+ return void 0;
929
+ }
843
930
 
844
931
  // src/analyzer/project-detector.ts
845
932
  var ProjectDetector = class {
@@ -878,11 +965,11 @@ var ProjectDetector = class {
878
965
  }
879
966
  const mainTsPath = path.join(root, sourceRoot, "main.ts");
880
967
  const orm = await detectORM(packageJson);
968
+ const database = detectDatabase(packageJson, orm);
881
969
  const authModulePath = path.join(root, sourceRoot, "auth");
882
- if (await fs.pathExists(authModulePath)) {
883
- errors.push("auth/ directory already exists (use --force to overwrite)");
884
- }
970
+ const authExists = await fs.pathExists(authModulePath);
885
971
  return {
972
+ authExists,
886
973
  root,
887
974
  sourceRoot,
888
975
  appModulePath,
@@ -890,6 +977,7 @@ var ProjectDetector = class {
890
977
  packageJsonPath,
891
978
  nestCliConfigPath,
892
979
  orm,
980
+ database,
893
981
  nestVersion: packageJson.dependencies?.["@nestjs/core"],
894
982
  typescriptVersion: packageJson.devDependencies?.["typescript"],
895
983
  isValid: errors.length === 0,
@@ -955,7 +1043,8 @@ function generateSecret(length = 32) {
955
1043
  }
956
1044
 
957
1045
  // src/cli/prompts.ts
958
- async function promptConfig(detectedORM) {
1046
+ async function promptConfig(detectedORM, detectedDB) {
1047
+ const dbLabel = detectedDB ? ` with ${detectedDB.charAt(0).toUpperCase() + detectedDB.slice(1)}` : "";
959
1048
  const answers = await import_inquirer.default.prompt([
960
1049
  {
961
1050
  type: "list",
@@ -1027,7 +1116,7 @@ async function promptConfig(detectedORM) {
1027
1116
  {
1028
1117
  type: "confirm",
1029
1118
  name: "useDetectedORM",
1030
- message: `Detected ${detectedORM.toUpperCase()}${detectedORM === "typeorm" ? " with PostgreSQL" : ""}. Use it?`,
1119
+ message: `Detected ${detectedORM.toUpperCase()}${dbLabel}. Use it?`,
1031
1120
  default: true,
1032
1121
  when: () => detectedORM !== "none"
1033
1122
  },
@@ -1053,7 +1142,7 @@ async function promptConfig(detectedORM) {
1053
1142
  ]);
1054
1143
  return answers;
1055
1144
  }
1056
- function buildConfig(answers, projectName, sourceRoot, detectedORM) {
1145
+ function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB) {
1057
1146
  const config = {
1058
1147
  projectName,
1059
1148
  sourceRoot,
@@ -1063,7 +1152,7 @@ function buildConfig(answers, projectName, sourceRoot, detectedORM) {
1063
1152
  roles: answers.roles || []
1064
1153
  },
1065
1154
  orm: answers.useDetectedORM !== false ? detectedORM : "typeorm",
1066
- database: answers.database || "postgres",
1155
+ database: answers.database || detectedDB || "postgres",
1067
1156
  features: {
1068
1157
  refreshTokens: answers.refreshTokens
1069
1158
  },
@@ -1205,12 +1294,26 @@ async function run(cwd = process.cwd()) {
1205
1294
  orm: projectInfo.orm,
1206
1295
  sourceRoot: projectInfo.sourceRoot
1207
1296
  });
1208
- const answers = await promptConfig(projectInfo.orm);
1297
+ if (projectInfo.authExists) {
1298
+ const inquirer2 = (await import("inquirer")).default;
1299
+ const { overwrite } = await inquirer2.prompt([{
1300
+ type: "confirm",
1301
+ name: "overwrite",
1302
+ message: "auth/ directory already exists. Overwrite existing files?",
1303
+ default: false
1304
+ }]);
1305
+ if (!overwrite) {
1306
+ console.log("\n\u23ED\uFE0F Cancelled. Existing auth module unchanged.\n");
1307
+ process.exit(0);
1308
+ }
1309
+ }
1310
+ const answers = await promptConfig(projectInfo.orm, projectInfo.database);
1209
1311
  const config = buildConfig(
1210
1312
  answers,
1211
1313
  projectInfo.root.split(/[/\\]/).pop() || "project",
1212
1314
  projectInfo.sourceRoot,
1213
- projectInfo.orm
1315
+ projectInfo.orm,
1316
+ projectInfo.database
1214
1317
  );
1215
1318
  console.log();
1216
1319
  console.log("\u2699\uFE0F Generating authentication module...");
@@ -1218,18 +1321,21 @@ async function run(cwd = process.cwd()) {
1218
1321
  const { Generator: Generator2 } = await Promise.resolve().then(() => (init_generator2(), generator_exports));
1219
1322
  const generator = new Generator2();
1220
1323
  const genSpinner = createSpinner("Generating files from templates...").start();
1221
- const result = await generator.generate(config, projectInfo);
1324
+ const result = await generator.generate(config, projectInfo, !!projectInfo.authExists);
1222
1325
  if (!result.success) {
1223
1326
  genSpinner.fail("Generation failed");
1224
1327
  showError("Failed to generate files", [result.error || "Unknown error"]);
1225
1328
  process.exit(1);
1226
1329
  }
1227
1330
  genSpinner.succeed(`Generated ${result.filesCreated.length} files`);
1331
+ if (result.filesSkipped.length > 0) {
1332
+ console.log(` \u26A0\uFE0F Skipped ${result.filesSkipped.length} existing file(s)`);
1333
+ }
1228
1334
  const astSpinner = createSpinner("Updating app.module.ts...").start();
1229
1335
  try {
1230
1336
  const { AppModuleUpdater: AppModuleUpdater2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
1231
1337
  const astUpdater = new AppModuleUpdater2(projectInfo.appModulePath);
1232
- await astUpdater.update();
1338
+ await astUpdater.update(config);
1233
1339
  await astUpdater.cleanupBackup();
1234
1340
  astSpinner.succeed("Updated app.module.ts");
1235
1341
  } catch (error) {