nestjs-doctor 0.3.1 → 0.4.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.
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  </p>
14
14
 
15
15
  <p align="center">
16
- 45 built-in rules across <b>security</b>, <b>performance</b>, <b>correctness</b>, and <b>architecture</b>. Outputs a <b>0-100 score</b> with actionable diagnostics. Zero config. Monorepo support. Built to catch the anti-patterns that AI-generated code loves to introduce.
16
+ 46 built-in rules across <b>security</b>, <b>performance</b>, <b>correctness</b>, and <b>architecture</b>. Outputs a <b>0-100 score</b> with actionable diagnostics. Zero config. Monorepo support. Built to catch the anti-patterns that AI-generated code loves to introduce.
17
17
  </p>
18
18
 
19
19
  ---
@@ -166,7 +166,7 @@ Or use a `"nestjs-doctor"` key in `package.json`.
166
166
  | Key | Type | Description |
167
167
  |-----|------|-------------|
168
168
  | `include` | `string[]` | Glob patterns to scan (default: `["**/*.ts"]`) |
169
- | `exclude` | `string[]` | Glob patterns to skip (default includes `node_modules`, `dist`, `build`, `coverage`, `*.spec.ts`, `*.test.ts`, `*.e2e-spec.ts`, `*.e2e-test.ts`, `*.d.ts`, `test/`, `tests/`, `__tests__/`, `__mocks__/`, `__fixtures__/`) |
169
+ | `exclude` | `string[]` | Glob patterns to skip (default includes `node_modules`, `dist`, `build`, `coverage`, `*.spec.ts`, `*.test.ts`, `*.e2e-spec.ts`, `*.e2e-test.ts`, `*.d.ts`, `test/`, `tests/`, `__tests__/`, `__mocks__/`, `__fixtures__/`, `mock/`, `mocks/`, `*.mock.ts`, `seeder/`, `seeders/`, `*.seed.ts`, `*.seeder.ts`) |
170
170
  | `minScore` | `number` | Minimum passing score (0-100). Exits with code 1 if below threshold |
171
171
  | `ignore.rules` | `string[]` | Rule IDs to suppress |
172
172
  | `ignore.files` | `string[]` | Glob patterns for files whose diagnostics are hidden |
@@ -234,7 +234,7 @@ mono.combined; // Merged DiagnoseResult
234
234
 
235
235
  ---
236
236
 
237
- ## Rules (45)
237
+ ## Rules (46)
238
238
 
239
239
  ### Security (9)
240
240
 
@@ -250,7 +250,7 @@ mono.combined; // Merged DiagnoseResult
250
250
  | `no-exposed-stack-trace` | warning | `error.stack` exposed in responses |
251
251
  | `require-auth-guard` | info | Controller without `@UseGuards()` |
252
252
 
253
- ### Correctness (13)
253
+ ### Correctness (14)
254
254
 
255
255
  | Rule | Severity | What it catches |
256
256
  |------|----------|-----------------|
@@ -264,7 +264,8 @@ mono.combined; // Merged DiagnoseResult
264
264
  | `prefer-readonly-injection` | warning | Constructor DI params missing `readonly` |
265
265
  | `require-lifecycle-interface` | warning | Lifecycle method without corresponding interface |
266
266
  | `no-empty-handlers` | warning | HTTP handler with empty body |
267
- | `no-async-without-await` | warning | Async function/method with no `await` |
267
+ | `no-async-without-await` | warning | Async function/method with no `await` or redundant `async` on `new Promise()` return |
268
+ | `prefer-await-in-handlers` | warning | Async HTTP handler missing await — risks broken exception filters and lost stack traces |
268
269
  | `no-duplicate-module-metadata` | warning | Duplicate entries in `@Module()` arrays |
269
270
  | `no-missing-module-decorator` | warning | Class named `*Module` without `@Module()` |
270
271
 
@@ -309,7 +309,7 @@ function isModule(cls) {
309
309
 
310
310
  //#endregion
311
311
  //#region src/rules/architecture/no-business-logic-in-controllers.ts
312
- const HTTP_DECORATORS$1 = new Set([
312
+ const HTTP_DECORATORS$3 = new Set([
313
313
  "Get",
314
314
  "Post",
315
315
  "Put",
@@ -331,7 +331,7 @@ const noBusinessLogicInControllers = {
331
331
  for (const cls of context.sourceFile.getClasses()) {
332
332
  if (!isController(cls)) continue;
333
333
  for (const method of cls.getMethods()) {
334
- if (!method.getDecorators().some((d) => HTTP_DECORATORS$1.has(d.getName()))) continue;
334
+ if (!method.getDecorators().some((d) => HTTP_DECORATORS$3.has(d.getName()))) continue;
335
335
  const body = method.getBody();
336
336
  if (!body) continue;
337
337
  const ifStatements = body.getDescendantsOfKind(ts_morph.SyntaxKind.IfStatement);
@@ -467,6 +467,18 @@ const noGodService = {
467
467
 
468
468
  //#endregion
469
469
  //#region src/rules/architecture/no-manual-instantiation.ts
470
+ const DI_ONLY_SUFFIXES = [
471
+ "Service",
472
+ "Repository",
473
+ "Gateway",
474
+ "Resolver"
475
+ ];
476
+ const CONTEXT_AWARE_SUFFIXES = [
477
+ "Guard",
478
+ "Interceptor",
479
+ "Pipe",
480
+ "Filter"
481
+ ];
470
482
  const noManualInstantiation = {
471
483
  meta: {
472
484
  id: "architecture/no-manual-instantiation",
@@ -479,7 +491,16 @@ const noManualInstantiation = {
479
491
  const newExpressions = context.sourceFile.getDescendantsOfKind(ts_morph.SyntaxKind.NewExpression);
480
492
  for (const expr of newExpressions) {
481
493
  const exprText = expr.getExpression().getText();
482
- if (exprText.endsWith("Service") || exprText.endsWith("Repository") || exprText.endsWith("Guard") || exprText.endsWith("Interceptor") || exprText.endsWith("Pipe") || exprText.endsWith("Filter") || exprText.endsWith("Gateway") || exprText.endsWith("Resolver")) context.report({
494
+ const isDiOnly = DI_ONLY_SUFFIXES.some((s) => exprText.endsWith(s));
495
+ const isContextAware = CONTEXT_AWARE_SUFFIXES.some((s) => exprText.endsWith(s));
496
+ if (!(isDiOnly || isContextAware)) continue;
497
+ if (isContextAware) {
498
+ if (expr.getFirstAncestorByKind(ts_morph.SyntaxKind.Decorator)) continue;
499
+ const inMethod = expr.getFirstAncestorByKind(ts_morph.SyntaxKind.MethodDeclaration);
500
+ const inConstructor = expr.getFirstAncestorByKind(ts_morph.SyntaxKind.Constructor);
501
+ if (!(inMethod || inConstructor)) continue;
502
+ }
503
+ context.report({
483
504
  filePath: context.filePath,
484
505
  message: `Manual instantiation of '${exprText}' detected. Use dependency injection instead.`,
485
506
  help: this.meta.help,
@@ -791,6 +812,23 @@ const requireModuleBoundaries = {
791
812
 
792
813
  //#endregion
793
814
  //#region src/rules/correctness/no-async-without-await.ts
815
+ const HTTP_DECORATORS$2 = new Set([
816
+ "Get",
817
+ "Post",
818
+ "Put",
819
+ "Delete",
820
+ "Patch",
821
+ "All",
822
+ "Head",
823
+ "Options"
824
+ ]);
825
+ function returnsNewPromise$1(body) {
826
+ return body.getDescendantsOfKind(ts_morph.SyntaxKind.ReturnStatement).some((ret) => {
827
+ const expr = ret.getExpression();
828
+ if (!expr || expr.getKind() !== ts_morph.SyntaxKind.NewExpression) return false;
829
+ return expr.asKindOrThrow(ts_morph.SyntaxKind.NewExpression).getExpression().getText() === "Promise";
830
+ });
831
+ }
794
832
  const noAsyncWithoutAwait = {
795
833
  meta: {
796
834
  id: "correctness/no-async-without-await",
@@ -802,6 +840,9 @@ const noAsyncWithoutAwait = {
802
840
  check(context) {
803
841
  for (const cls of context.sourceFile.getClasses()) for (const method of cls.getMethods()) {
804
842
  if (!method.isAsync()) continue;
843
+ if (isController(cls)) {
844
+ if (method.getDecorators().some((d) => HTTP_DECORATORS$2.has(d.getName()))) continue;
845
+ }
805
846
  const body = method.getBody();
806
847
  if (!body) continue;
807
848
  if (body.getDescendantsOfKind(ts_morph.SyntaxKind.AwaitExpression).filter((expr) => {
@@ -811,13 +852,23 @@ const noAsyncWithoutAwait = {
811
852
  parent = parent.getParent();
812
853
  }
813
854
  return true;
814
- }).length === 0) context.report({
815
- filePath: context.filePath,
816
- message: `Async method '${method.getName()}()' has no await expression.`,
817
- help: this.meta.help,
818
- line: method.getStartLineNumber(),
819
- column: 1
820
- });
855
+ }).length === 0) {
856
+ const name = method.getName();
857
+ if (returnsNewPromise$1(body)) context.report({
858
+ filePath: context.filePath,
859
+ message: `Async method '${name}()' returns a Promise directly — remove the async keyword.`,
860
+ help: "The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.",
861
+ line: method.getStartLineNumber(),
862
+ column: 1
863
+ });
864
+ else context.report({
865
+ filePath: context.filePath,
866
+ message: `Async method '${name}()' has no await expression.`,
867
+ help: this.meta.help,
868
+ line: method.getStartLineNumber(),
869
+ column: 1
870
+ });
871
+ }
821
872
  }
822
873
  for (const fn of context.sourceFile.getFunctions()) {
823
874
  if (!fn.isAsync()) continue;
@@ -830,13 +881,23 @@ const noAsyncWithoutAwait = {
830
881
  parent = parent.getParent();
831
882
  }
832
883
  return true;
833
- }).length === 0) context.report({
834
- filePath: context.filePath,
835
- message: `Async function '${fn.getName() ?? "anonymous"}()' has no await expression.`,
836
- help: this.meta.help,
837
- line: fn.getStartLineNumber(),
838
- column: 1
839
- });
884
+ }).length === 0) {
885
+ const name = fn.getName() ?? "anonymous";
886
+ if (returnsNewPromise$1(body)) context.report({
887
+ filePath: context.filePath,
888
+ message: `Async function '${name}()' returns a Promise directly — remove the async keyword.`,
889
+ help: "The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.",
890
+ line: fn.getStartLineNumber(),
891
+ column: 1
892
+ });
893
+ else context.report({
894
+ filePath: context.filePath,
895
+ message: `Async function '${name}()' has no await expression.`,
896
+ help: this.meta.help,
897
+ line: fn.getStartLineNumber(),
898
+ column: 1
899
+ });
900
+ }
840
901
  }
841
902
  }
842
903
  };
@@ -935,7 +996,7 @@ const noDuplicateRoutes = {
935
996
 
936
997
  //#endregion
937
998
  //#region src/rules/correctness/no-empty-handlers.ts
938
- const HTTP_DECORATORS = new Set([
999
+ const HTTP_DECORATORS$1 = new Set([
939
1000
  "Get",
940
1001
  "Post",
941
1002
  "Put",
@@ -957,7 +1018,7 @@ const noEmptyHandlers = {
957
1018
  for (const cls of context.sourceFile.getClasses()) {
958
1019
  if (!isController(cls)) continue;
959
1020
  for (const method of cls.getMethods()) {
960
- if (!method.getDecorators().some((d) => HTTP_DECORATORS.has(d.getName()))) continue;
1021
+ if (!method.getDecorators().some((d) => HTTP_DECORATORS$1.has(d.getName()))) continue;
961
1022
  const body = method.getBody();
962
1023
  if (!body) continue;
963
1024
  if (body.getStatements().length === 0) context.report({
@@ -1148,6 +1209,69 @@ const noMissingPipeMethod = {
1148
1209
  }
1149
1210
  };
1150
1211
 
1212
+ //#endregion
1213
+ //#region src/rules/correctness/prefer-await-in-handlers.ts
1214
+ const HTTP_DECORATORS = new Set([
1215
+ "Get",
1216
+ "Post",
1217
+ "Put",
1218
+ "Delete",
1219
+ "Patch",
1220
+ "All",
1221
+ "Head",
1222
+ "Options"
1223
+ ]);
1224
+ function returnsNewPromise(body) {
1225
+ return body.getDescendantsOfKind(ts_morph.SyntaxKind.ReturnStatement).some((ret) => {
1226
+ const expr = ret.getExpression();
1227
+ if (!expr || expr.getKind() !== ts_morph.SyntaxKind.NewExpression) return false;
1228
+ return expr.asKindOrThrow(ts_morph.SyntaxKind.NewExpression).getExpression().getText() === "Promise";
1229
+ });
1230
+ }
1231
+ const preferAwaitInHandlers = {
1232
+ meta: {
1233
+ id: "correctness/prefer-await-in-handlers",
1234
+ category: "correctness",
1235
+ severity: "warning",
1236
+ description: "Async HTTP handler missing await — risks broken exception filters and lost stack traces",
1237
+ help: "Add await when calling services. This ensures exception filters trigger correctly, stack traces point to the handler, and error handling is consistent across handlers."
1238
+ },
1239
+ check(context) {
1240
+ for (const cls of context.sourceFile.getClasses()) {
1241
+ if (!isController(cls)) continue;
1242
+ for (const method of cls.getMethods()) {
1243
+ if (!(method.getDecorators().some((d) => HTTP_DECORATORS.has(d.getName())) && method.isAsync())) continue;
1244
+ const body = method.getBody();
1245
+ if (!body) continue;
1246
+ if (body.getDescendantsOfKind(ts_morph.SyntaxKind.AwaitExpression).filter((expr) => {
1247
+ let parent = expr.getParent();
1248
+ while (parent && parent !== body) {
1249
+ if (parent.getKind() === ts_morph.SyntaxKind.ArrowFunction || parent.getKind() === ts_morph.SyntaxKind.FunctionExpression || parent.getKind() === ts_morph.SyntaxKind.FunctionDeclaration) return false;
1250
+ parent = parent.getParent();
1251
+ }
1252
+ return true;
1253
+ }).length === 0) {
1254
+ const name = method.getName();
1255
+ if (returnsNewPromise(body)) context.report({
1256
+ filePath: context.filePath,
1257
+ message: `Handler '${name}()' returns a Promise directly — remove async and return the promise, or await the resolved value.`,
1258
+ help: this.meta.help,
1259
+ line: method.getStartLineNumber(),
1260
+ column: 1
1261
+ });
1262
+ else context.report({
1263
+ filePath: context.filePath,
1264
+ message: `Async handler '${name}()' does not await any expression.`,
1265
+ help: this.meta.help,
1266
+ line: method.getStartLineNumber(),
1267
+ column: 1
1268
+ });
1269
+ }
1270
+ }
1271
+ }
1272
+ }
1273
+ };
1274
+
1151
1275
  //#endregion
1152
1276
  //#region src/rules/correctness/prefer-readonly-injection.ts
1153
1277
  const preferReadonlyInjection = {
@@ -2066,6 +2190,7 @@ const allRules = [
2066
2190
  noMissingFilterCatch,
2067
2191
  noMissingInterceptorMethod,
2068
2192
  noAsyncWithoutAwait,
2193
+ preferAwaitInHandlers,
2069
2194
  noDuplicateModuleMetadata,
2070
2195
  noMissingModuleDecorator,
2071
2196
  requireInjectDecorator,
@@ -2177,7 +2302,14 @@ const DEFAULT_CONFIG = {
2177
2302
  "**/tests/**",
2178
2303
  "**/__tests__/**",
2179
2304
  "**/__mocks__/**",
2180
- "**/__fixtures__/**"
2305
+ "**/__fixtures__/**",
2306
+ "**/mock/**",
2307
+ "**/mocks/**",
2308
+ "**/*.mock.ts",
2309
+ "**/seeder/**",
2310
+ "**/seeders/**",
2311
+ "**/*.seed.ts",
2312
+ "**/*.seeder.ts"
2181
2313
  ]
2182
2314
  };
2183
2315
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":";;;UASiB,UAAA;EAChB,gBAAA,EAAkB,gBAAA;EAClB,WAAA;EACA,OAAA;EACA,QAAA;EACA,OAAA;EACA,IAAA;EACA,SAAA;AAAA;AAAA,UAGgB,WAAA;EAChB,KAAA,EAAO,GAAA,SAAY,GAAA;EACnB,OAAA,EAAS,GAAA,SAAY,UAAA;EACrB,gBAAA,EAAkB,GAAA,SAAY,UAAA;AAAA;;;UCjBd,YAAA;EAChB,gBAAA,EAAkB,gBAAA;EAClB,YAAA;EACA,QAAA;EACA,IAAA;EACA,iBAAA;AAAA;;;KCVW,QAAA;AAAA,KACA,QAAA;AAAA,UAMK,UAAA;EAChB,QAAA,EAAU,QAAA;EACV,MAAA;EACA,QAAA;EACA,IAAA;EACA,IAAA;EACA,OAAA;EACA,IAAA;EACA,QAAA,EAAU,QAAA;AAAA;;;UCbM,YAAA;EAChB,OAAA;EACA,QAAA,GAAW,QAAA;AAAA;AAAA,UAGK,wBAAA;EAChB,KAAA;EACA,KAAA;AAAA;AAAA,UAGgB,kBAAA;EAChB,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,QAAA;EAC5B,OAAA;EACA,MAAA,GAAS,wBAAA;EACT,OAAA;EACA,QAAA;EACA,KAAA,GAAQ,MAAA,SAAe,YAAA;EACvB,UAAA;IACC,kBAAA;IACA,gBAAA;IACA,iBAAA;IACA,cAAA;EAAA;AAAA;;;KCjBU,SAAA;AAAA,UAEK,QAAA;EAChB,QAAA,EAAU,QAAA;EACV,WAAA;EACA,IAAA;EACA,EAAA;EACA,KAAA,GAAQ,SAAA;EACR,QAAA,EAAU,QAAA;AAAA;AAAA,UAGM,WAAA;EAChB,QAAA;EACA,MAAA,CAAO,UAAA,EAAY,IAAA,CAAK,UAAA;EACxB,UAAA,EAAY,UAAA;AAAA;AAAA,UAGI,kBAAA;EAChB,MAAA,EAAQ,kBAAA;EACR,KAAA;EACA,WAAA,EAAa,WAAA;EACb,OAAA,EAAS,OAAA;EACT,SAAA,EAAW,GAAA,SAAY,YAAA;EACvB,MAAA,CAAO,UAAA,EAAY,IAAA,CAAK,UAAA;AAAA;AAAA,UAGR,IAAA;EAChB,KAAA,CAAM,OAAA,EAAS,WAAA;EACf,IAAA,EAAM,QAAA;AAAA;AAAA,UAGU,WAAA;EAChB,KAAA,CAAM,OAAA,EAAS,kBAAA;EACf,IAAA,EAAM,QAAA;AAAA;AAAA,KAGK,OAAA,GAAU,IAAA,GAAO,WAAA;;;iBCkEb,QAAA,CAAA,GAAY,OAAA;;;cC5Gf,iBAAA,SAA0B,KAAA;cAC1B,OAAA;AAAA;AAAA,cAMA,kBAAA,SAA2B,iBAAA;cAC3B,OAAA;AAAA;AAAA,cAMA,SAAA,SAAkB,iBAAA;cAClB,OAAA;AAAA;AAAA,cAMA,eAAA,SAAwB,iBAAA;cACxB,OAAA;AAAA;;;UCpBI,KAAA;EAChB,KAAA;EACA,KAAA;AAAA;AAAA,UAGgB,WAAA;EAChB,SAAA;EACA,SAAA;EACA,WAAA;EACA,IAAA;EACA,WAAA;EACA,GAAA;AAAA;AAAA,UAGgB,eAAA;EAChB,UAAA,EAAY,MAAA,CAAO,QAAA;EACnB,MAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGgB,aAAA;EAChB,KAAA;EACA,MAAA;AAAA;AAAA,UAGgB,cAAA;EAChB,WAAA,EAAa,UAAA;EACb,SAAA;EACA,OAAA,EAAS,WAAA;EACT,UAAA,EAAY,aAAA;EACZ,KAAA,EAAO,KAAA;EACP,OAAA,EAAS,eAAA;AAAA;AAAA,UAGO,gBAAA;EAChB,IAAA;EACA,MAAA,EAAQ,cAAA;AAAA;AAAA,UAGQ,cAAA;EAChB,QAAA,EAAU,cAAA;EACV,SAAA;EACA,UAAA;EACA,WAAA,EAAa,gBAAA;AAAA;;;;;;;;;;;iBCiBQ,QAAA,CACrB,IAAA,UACA,OAAA;EAAW,MAAA;AAAA,IAAsB,OAAA,CAFJ,cAAA;AR7C9B;;;;;;;;;;;AAAA,iBQgEsB,gBAAA,CACrB,IAAA,UACA,OAAA;EAAW,MAAA;AAAA,IAAsB,OAAA,CAFI,cAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/engine/module-graph.ts","../../src/engine/type-resolver.ts","../../src/types/diagnostic.ts","../../src/types/config.ts","../../src/rules/types.ts","../../src/rules/index.ts","../../src/types/errors.ts","../../src/types/result.ts","../../src/api/index.ts"],"mappings":"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"}
@@ -280,7 +280,7 @@ function isModule(cls) {
280
280
 
281
281
  //#endregion
282
282
  //#region src/rules/architecture/no-business-logic-in-controllers.ts
283
- const HTTP_DECORATORS$1 = new Set([
283
+ const HTTP_DECORATORS$3 = new Set([
284
284
  "Get",
285
285
  "Post",
286
286
  "Put",
@@ -302,7 +302,7 @@ const noBusinessLogicInControllers = {
302
302
  for (const cls of context.sourceFile.getClasses()) {
303
303
  if (!isController(cls)) continue;
304
304
  for (const method of cls.getMethods()) {
305
- if (!method.getDecorators().some((d) => HTTP_DECORATORS$1.has(d.getName()))) continue;
305
+ if (!method.getDecorators().some((d) => HTTP_DECORATORS$3.has(d.getName()))) continue;
306
306
  const body = method.getBody();
307
307
  if (!body) continue;
308
308
  const ifStatements = body.getDescendantsOfKind(SyntaxKind.IfStatement);
@@ -438,6 +438,18 @@ const noGodService = {
438
438
 
439
439
  //#endregion
440
440
  //#region src/rules/architecture/no-manual-instantiation.ts
441
+ const DI_ONLY_SUFFIXES = [
442
+ "Service",
443
+ "Repository",
444
+ "Gateway",
445
+ "Resolver"
446
+ ];
447
+ const CONTEXT_AWARE_SUFFIXES = [
448
+ "Guard",
449
+ "Interceptor",
450
+ "Pipe",
451
+ "Filter"
452
+ ];
441
453
  const noManualInstantiation = {
442
454
  meta: {
443
455
  id: "architecture/no-manual-instantiation",
@@ -450,7 +462,16 @@ const noManualInstantiation = {
450
462
  const newExpressions = context.sourceFile.getDescendantsOfKind(SyntaxKind.NewExpression);
451
463
  for (const expr of newExpressions) {
452
464
  const exprText = expr.getExpression().getText();
453
- if (exprText.endsWith("Service") || exprText.endsWith("Repository") || exprText.endsWith("Guard") || exprText.endsWith("Interceptor") || exprText.endsWith("Pipe") || exprText.endsWith("Filter") || exprText.endsWith("Gateway") || exprText.endsWith("Resolver")) context.report({
465
+ const isDiOnly = DI_ONLY_SUFFIXES.some((s) => exprText.endsWith(s));
466
+ const isContextAware = CONTEXT_AWARE_SUFFIXES.some((s) => exprText.endsWith(s));
467
+ if (!(isDiOnly || isContextAware)) continue;
468
+ if (isContextAware) {
469
+ if (expr.getFirstAncestorByKind(SyntaxKind.Decorator)) continue;
470
+ const inMethod = expr.getFirstAncestorByKind(SyntaxKind.MethodDeclaration);
471
+ const inConstructor = expr.getFirstAncestorByKind(SyntaxKind.Constructor);
472
+ if (!(inMethod || inConstructor)) continue;
473
+ }
474
+ context.report({
454
475
  filePath: context.filePath,
455
476
  message: `Manual instantiation of '${exprText}' detected. Use dependency injection instead.`,
456
477
  help: this.meta.help,
@@ -762,6 +783,23 @@ const requireModuleBoundaries = {
762
783
 
763
784
  //#endregion
764
785
  //#region src/rules/correctness/no-async-without-await.ts
786
+ const HTTP_DECORATORS$2 = new Set([
787
+ "Get",
788
+ "Post",
789
+ "Put",
790
+ "Delete",
791
+ "Patch",
792
+ "All",
793
+ "Head",
794
+ "Options"
795
+ ]);
796
+ function returnsNewPromise$1(body) {
797
+ return body.getDescendantsOfKind(SyntaxKind.ReturnStatement).some((ret) => {
798
+ const expr = ret.getExpression();
799
+ if (!expr || expr.getKind() !== SyntaxKind.NewExpression) return false;
800
+ return expr.asKindOrThrow(SyntaxKind.NewExpression).getExpression().getText() === "Promise";
801
+ });
802
+ }
765
803
  const noAsyncWithoutAwait = {
766
804
  meta: {
767
805
  id: "correctness/no-async-without-await",
@@ -773,6 +811,9 @@ const noAsyncWithoutAwait = {
773
811
  check(context) {
774
812
  for (const cls of context.sourceFile.getClasses()) for (const method of cls.getMethods()) {
775
813
  if (!method.isAsync()) continue;
814
+ if (isController(cls)) {
815
+ if (method.getDecorators().some((d) => HTTP_DECORATORS$2.has(d.getName()))) continue;
816
+ }
776
817
  const body = method.getBody();
777
818
  if (!body) continue;
778
819
  if (body.getDescendantsOfKind(SyntaxKind.AwaitExpression).filter((expr) => {
@@ -782,13 +823,23 @@ const noAsyncWithoutAwait = {
782
823
  parent = parent.getParent();
783
824
  }
784
825
  return true;
785
- }).length === 0) context.report({
786
- filePath: context.filePath,
787
- message: `Async method '${method.getName()}()' has no await expression.`,
788
- help: this.meta.help,
789
- line: method.getStartLineNumber(),
790
- column: 1
791
- });
826
+ }).length === 0) {
827
+ const name = method.getName();
828
+ if (returnsNewPromise$1(body)) context.report({
829
+ filePath: context.filePath,
830
+ message: `Async method '${name}()' returns a Promise directly — remove the async keyword.`,
831
+ help: "The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.",
832
+ line: method.getStartLineNumber(),
833
+ column: 1
834
+ });
835
+ else context.report({
836
+ filePath: context.filePath,
837
+ message: `Async method '${name}()' has no await expression.`,
838
+ help: this.meta.help,
839
+ line: method.getStartLineNumber(),
840
+ column: 1
841
+ });
842
+ }
792
843
  }
793
844
  for (const fn of context.sourceFile.getFunctions()) {
794
845
  if (!fn.isAsync()) continue;
@@ -801,13 +852,23 @@ const noAsyncWithoutAwait = {
801
852
  parent = parent.getParent();
802
853
  }
803
854
  return true;
804
- }).length === 0) context.report({
805
- filePath: context.filePath,
806
- message: `Async function '${fn.getName() ?? "anonymous"}()' has no await expression.`,
807
- help: this.meta.help,
808
- line: fn.getStartLineNumber(),
809
- column: 1
810
- });
855
+ }).length === 0) {
856
+ const name = fn.getName() ?? "anonymous";
857
+ if (returnsNewPromise$1(body)) context.report({
858
+ filePath: context.filePath,
859
+ message: `Async function '${name}()' returns a Promise directly — remove the async keyword.`,
860
+ help: "The async keyword is unnecessary when you are already constructing a Promise manually. Remove async to avoid double-wrapping.",
861
+ line: fn.getStartLineNumber(),
862
+ column: 1
863
+ });
864
+ else context.report({
865
+ filePath: context.filePath,
866
+ message: `Async function '${name}()' has no await expression.`,
867
+ help: this.meta.help,
868
+ line: fn.getStartLineNumber(),
869
+ column: 1
870
+ });
871
+ }
811
872
  }
812
873
  }
813
874
  };
@@ -906,7 +967,7 @@ const noDuplicateRoutes = {
906
967
 
907
968
  //#endregion
908
969
  //#region src/rules/correctness/no-empty-handlers.ts
909
- const HTTP_DECORATORS = new Set([
970
+ const HTTP_DECORATORS$1 = new Set([
910
971
  "Get",
911
972
  "Post",
912
973
  "Put",
@@ -928,7 +989,7 @@ const noEmptyHandlers = {
928
989
  for (const cls of context.sourceFile.getClasses()) {
929
990
  if (!isController(cls)) continue;
930
991
  for (const method of cls.getMethods()) {
931
- if (!method.getDecorators().some((d) => HTTP_DECORATORS.has(d.getName()))) continue;
992
+ if (!method.getDecorators().some((d) => HTTP_DECORATORS$1.has(d.getName()))) continue;
932
993
  const body = method.getBody();
933
994
  if (!body) continue;
934
995
  if (body.getStatements().length === 0) context.report({
@@ -1119,6 +1180,69 @@ const noMissingPipeMethod = {
1119
1180
  }
1120
1181
  };
1121
1182
 
1183
+ //#endregion
1184
+ //#region src/rules/correctness/prefer-await-in-handlers.ts
1185
+ const HTTP_DECORATORS = new Set([
1186
+ "Get",
1187
+ "Post",
1188
+ "Put",
1189
+ "Delete",
1190
+ "Patch",
1191
+ "All",
1192
+ "Head",
1193
+ "Options"
1194
+ ]);
1195
+ function returnsNewPromise(body) {
1196
+ return body.getDescendantsOfKind(SyntaxKind.ReturnStatement).some((ret) => {
1197
+ const expr = ret.getExpression();
1198
+ if (!expr || expr.getKind() !== SyntaxKind.NewExpression) return false;
1199
+ return expr.asKindOrThrow(SyntaxKind.NewExpression).getExpression().getText() === "Promise";
1200
+ });
1201
+ }
1202
+ const preferAwaitInHandlers = {
1203
+ meta: {
1204
+ id: "correctness/prefer-await-in-handlers",
1205
+ category: "correctness",
1206
+ severity: "warning",
1207
+ description: "Async HTTP handler missing await — risks broken exception filters and lost stack traces",
1208
+ help: "Add await when calling services. This ensures exception filters trigger correctly, stack traces point to the handler, and error handling is consistent across handlers."
1209
+ },
1210
+ check(context) {
1211
+ for (const cls of context.sourceFile.getClasses()) {
1212
+ if (!isController(cls)) continue;
1213
+ for (const method of cls.getMethods()) {
1214
+ if (!(method.getDecorators().some((d) => HTTP_DECORATORS.has(d.getName())) && method.isAsync())) continue;
1215
+ const body = method.getBody();
1216
+ if (!body) continue;
1217
+ if (body.getDescendantsOfKind(SyntaxKind.AwaitExpression).filter((expr) => {
1218
+ let parent = expr.getParent();
1219
+ while (parent && parent !== body) {
1220
+ if (parent.getKind() === SyntaxKind.ArrowFunction || parent.getKind() === SyntaxKind.FunctionExpression || parent.getKind() === SyntaxKind.FunctionDeclaration) return false;
1221
+ parent = parent.getParent();
1222
+ }
1223
+ return true;
1224
+ }).length === 0) {
1225
+ const name = method.getName();
1226
+ if (returnsNewPromise(body)) context.report({
1227
+ filePath: context.filePath,
1228
+ message: `Handler '${name}()' returns a Promise directly — remove async and return the promise, or await the resolved value.`,
1229
+ help: this.meta.help,
1230
+ line: method.getStartLineNumber(),
1231
+ column: 1
1232
+ });
1233
+ else context.report({
1234
+ filePath: context.filePath,
1235
+ message: `Async handler '${name}()' does not await any expression.`,
1236
+ help: this.meta.help,
1237
+ line: method.getStartLineNumber(),
1238
+ column: 1
1239
+ });
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ };
1245
+
1122
1246
  //#endregion
1123
1247
  //#region src/rules/correctness/prefer-readonly-injection.ts
1124
1248
  const preferReadonlyInjection = {
@@ -2037,6 +2161,7 @@ const allRules = [
2037
2161
  noMissingFilterCatch,
2038
2162
  noMissingInterceptorMethod,
2039
2163
  noAsyncWithoutAwait,
2164
+ preferAwaitInHandlers,
2040
2165
  noDuplicateModuleMetadata,
2041
2166
  noMissingModuleDecorator,
2042
2167
  requireInjectDecorator,
@@ -2148,7 +2273,14 @@ const DEFAULT_CONFIG = {
2148
2273
  "**/tests/**",
2149
2274
  "**/__tests__/**",
2150
2275
  "**/__mocks__/**",
2151
- "**/__fixtures__/**"
2276
+ "**/__fixtures__/**",
2277
+ "**/mock/**",
2278
+ "**/mocks/**",
2279
+ "**/*.mock.ts",
2280
+ "**/seeder/**",
2281
+ "**/seeders/**",
2282
+ "**/*.seed.ts",
2283
+ "**/*.seeder.ts"
2152
2284
  ]
2153
2285
  };
2154
2286