@tianjos/eslint-plugin-elegant 0.3.2 → 0.5.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
@@ -68,13 +68,16 @@ A complete, copy-pasteable example (including test-file overrides) lives in
68
68
 
69
69
  ## Rules
70
70
 
71
- The `recommended` config enables every custom rule plus the native
72
- [`max-params`](https://eslint.org/docs/latest/rules/max-params) rule.
71
+ The `recommended` config enables every custom rule plus two native ones,
72
+ [`max-params`](https://eslint.org/docs/latest/rules/max-params) and
73
+ [`no-else-return`](https://eslint.org/docs/latest/rules/no-else-return).
73
74
 
74
75
  | Rule | Source | What it catches | `recommended` |
75
76
  | -------------------------------------- | ------ | ------------------------------------------------------------------------------- | ------------- |
76
77
  | `elegant/no-boolean-param` | custom | Boolean parameters (flag arguments) on functions, methods, and constructors | `error` |
77
78
  | `elegant/max-class-methods` | custom | Classes with more methods than the configured `max` (constructors excluded) | `warn` (max 10) |
79
+ | `elegant/max-class-dependencies` | custom | Classes depending on more distinct collaborators than `max` (constructor injections plus `new`) | `warn` (max 4) |
80
+ | `elegant/max-class-fields` | custom | Classes holding more instance fields than `max` (declared fields plus parameter properties) | `warn` (max 5) |
78
81
  | `elegant/no-type-assertion` | custom | `value as T` and `<T>value` assertions (`as const` is allowed) | `error` |
79
82
  | `elegant/no-null-return` | custom | `return null` statements | `error` |
80
83
  | `elegant/no-public-mutable-props` | custom | Public, non-`readonly` class properties and public constructor parameter props | `error` |
@@ -83,7 +86,15 @@ The `recommended` config enables every custom rule plus the native
83
86
  | `elegant/no-instanceof` | custom | Use of the `instanceof` operator | `error` |
84
87
  | `elegant/no-static-members` | custom | Static methods, properties, accessors, and blocks (`allowReadonly` to permit constants) | `error` |
85
88
  | `elegant/no-null` | custom | The `null` literal as a value (type annotations and direct `return null` excepted) | `error` |
89
+ | `elegant/no-comments-in-function-body` | custom | Comments inside function bodies (directives and empty blocks excepted) | `error` |
90
+ | `elegant/no-else-after-throw` | custom | An `else` branch when the `then` branch always throws | `error` |
91
+ | `elegant/no-interpolated-log-message` | custom | Log messages built by interpolation or concatenation | `error` |
92
+ | `elegant/max-returns` | custom | Functions returning from more places than `max` | `warn` (max 3) |
93
+ | `elegant/no-property-alias` | custom | Locals that only rename a property of an object already in hand | `error` |
94
+ | `elegant/no-property-destructuring` | custom | Destructuring an object already in hand into locals | `error` |
95
+ | `elegant/no-anonymous-param-type` | custom | Parameters typed as an anonymous shape of `minMembers` or more properties | `error` (minMembers 2) |
86
96
  | `max-params` | native | Functions declaring more than `max` parameters | `warn` (max 3) |
97
+ | `no-else-return` | native | An `else` branch when the `then` branch always returns (`allowElseIf: false`) | `error` |
87
98
 
88
99
  ### Rule details
89
100
 
@@ -98,6 +109,66 @@ intention-revealing functions or an options object. Flags both annotated
98
109
  A proxy for the Single Responsibility Principle. Constructors are not counted;
99
110
  getters and setters are. Configurable via `{ max: number }` (default `10`).
100
111
 
112
+ #### `max-class-dependencies`
113
+
114
+ The coupling counterpart to `max-class-methods`: a class that needs six
115
+ collaborators to do its job is coordinating, not modelling. Counts the distinct
116
+ types annotated on constructor parameters plus every type instantiated with
117
+ `new` inside the class body — so a dependency hidden behind `new HttpClient()`
118
+ weighs the same as an injected one.
119
+
120
+ De-duplication keeps type arguments, so `Repository<Order>` and
121
+ `Repository<Customer>` count as two collaborators while the same `Clock`
122
+ injected twice counts as one. Nested classes are budgeted independently of their
123
+ host.
124
+
125
+ Three things never count: primitives and inline types (they are not type
126
+ references), a default list of ambient built-ins (`Date`, `Map`, `Set`,
127
+ `Promise`, `Error`, `Array`, `RegExp`, `URL`, `WeakMap`, `WeakSet`), and
128
+ exceptions raised with `throw new ...`. That last exclusion is what makes the
129
+ rule usable in NestJS, where `throw new NotFoundException()` is routine and says
130
+ nothing about a class's design.
131
+
132
+ Configurable via `{ max: number, ignore: string[] }` (default `max: 4`).
133
+ `ignore` adds to the built-in list — reach for it when an ambient concern such
134
+ as `Logger` or `ConfigService` is in every constructor and you would rather not
135
+ budget for it:
136
+
137
+ ```js
138
+ 'elegant/max-class-dependencies': ['warn', { max: 4, ignore: ['Logger'] }],
139
+ ```
140
+
141
+ #### `max-class-fields`
142
+
143
+ The third axis of class size, after methods and collaborators: a class carrying
144
+ a dozen fields is a record with a namespace, not a model. Counts instance fields
145
+ declared in the body — plain, `abstract`, or `accessor` — plus every constructor
146
+ parameter property. Methods and accessors belong to `max-class-methods`, and
147
+ `static` members to `no-static-members`, so neither is counted here.
148
+
149
+ Decorated properties are skipped by default. `@Column`, `@IsString` and
150
+ `@ApiProperty` map a field to a table or a payload, so a DTO or an ORM entity
151
+ declares one field per column by design and has no business inside a budget:
152
+
153
+ ```ts
154
+ class CreateOrderDto {
155
+ @IsString() customerId: string; // not counted
156
+ @IsInt() quantity: number; // not counted
157
+ }
158
+ ```
159
+
160
+ **The exemption stops at the constructor.** A decorator on a parameter is
161
+ injection, not mapping, so `@Inject(TOKEN) private readonly repo: Repo` stays
162
+ inside the budget — otherwise a service wired entirely through tokens would
163
+ count zero fields, which is exactly the class the rule exists to catch. Set
164
+ `{ ignoreDecorated: false }` to budget mapped properties too.
165
+
166
+ Configurable via `{ max: number, ignoreDecorated: boolean }` (default `max: 5`,
167
+ `ignoreDecorated: true`). The default leaves room for the four collaborators
168
+ `max-class-dependencies` allows plus one field of genuine state; past that the
169
+ two rules deliberately overlap, because a class over both budgets is over-sized
170
+ on both axes.
171
+
101
172
  #### `no-type-assertion`
102
173
 
103
174
  Assertions silence the type checker. Reach for a type guard, a generic, or a
@@ -153,16 +224,311 @@ or `undefined`. `null` in type positions (`string | null`) and a direct
153
224
  flag idioms like `JSON.stringify(x, null, 2)` — relax it in the files where you
154
225
  interoperate with null-based APIs.
155
226
 
227
+ #### `no-comments-in-function-body`
228
+
229
+ A comment inside a body is usually a name that never got written: it labels a
230
+ run of statements that wanted to be its own function. Move the explanation to a
231
+ docblock above the function, or extract what it describes and let the call read
232
+ as the sentence the comment was trying to be.
233
+
234
+ Applies to every function with a block body — methods, constructors, function
235
+ declarations, and arrow functions. Comments outside a body are untouched, so
236
+ docblocks, module-level notes, and comments between class members are fine. Each
237
+ comment is attributed to the innermost function containing it, so one in a
238
+ nested arrow is reported once, against that arrow.
239
+
240
+ Two things are never reported:
241
+
242
+ - **Directives**, which the toolchain reads rather than a human:
243
+ `eslint-disable*`, `@ts-expect-error`, `@ts-ignore`, `prettier-ignore`,
244
+ `istanbul ignore`, `c8 ignore`, `v8 ignore`, `webpackChunkName`,
245
+ `@vite-ignore`. Extend the list with `{ allow: string[] }` for project
246
+ conventions such as `@codegen`.
247
+ - **Comments alone in an empty block**, where there is no code to name and the
248
+ comment is the only thing explaining the silence. The check looks at the
249
+ innermost block, not the function, so an empty `catch` keeps its note even
250
+ inside a busy function:
251
+
252
+ ```ts
253
+ function run(): void {
254
+ try {
255
+ go();
256
+ } catch {
257
+ // the failure is expected here
258
+ }
259
+ }
260
+ ```
261
+
262
+ #### `no-else-after-throw`
263
+
264
+ When the `then` branch throws, control never reaches what follows, so `else`
265
+ carries no information and only deepens nesting. Drop it and let the alternative
266
+ sit at the outer level, where it reads as the normal path rather than one of two
267
+ symmetric cases:
268
+
269
+ ```ts
270
+ // before
271
+ if (amount < 0) {
272
+ throw new NegativeAmount(amount);
273
+ } else {
274
+ process(amount);
275
+ }
276
+
277
+ // after
278
+ if (amount < 0) {
279
+ throw new NegativeAmount(amount);
280
+ }
281
+ process(amount);
282
+ ```
283
+
284
+ A branch counts as always throwing when it is a bare `throw` or a block whose
285
+ **last** statement is one. The check does not recurse, which is deliberate: a
286
+ block ending in a nested `if` may or may not throw, and there `else` still says
287
+ something.
288
+
289
+ `else if` is flagged too, since the same rewrite applies. The rule has no
290
+ options and no autofix — dedenting a block reliably is the formatter's job, not
291
+ a linter's.
292
+
293
+ Its sibling for `return` is the native
294
+ [`no-else-return`](https://eslint.org/docs/latest/rules/no-else-return), which
295
+ `recommended` enables with `allowElseIf: false` to match. Prefer the native
296
+ rule's defaults? Override it in one line:
297
+
298
+ ```js
299
+ 'no-else-return': 'error',
300
+ ```
301
+
302
+ #### `no-interpolated-log-message`
303
+
304
+ A log message should be a constant, with everything that varies passed as
305
+ structured data. This is not a style preference: `` `order ${id} confirmed` ``
306
+ produces one distinct message per order, which no aggregator can group, and it
307
+ buries `id` inside prose instead of leaving it as a field you can filter on.
308
+
309
+ ```ts
310
+ // before — N messages, and the id is not queryable
311
+ this.logger.log(`order ${id} confirmed for ${customer}`);
312
+
313
+ // after — one message, two fields
314
+ this.logger.log('order confirmed', { orderId: id, customer });
315
+ ```
316
+
317
+ The rule looks only at the **message argument**, which it takes to be the first
318
+ argument that is not an object literal. That lands on the message under either
319
+ convention — `info(message, data)` as in NestJS and winston, `info(data,
320
+ message)` as in pino — so the rule never dictates where your data goes. An
321
+ interpolated *later* argument is left alone, since that position is context
322
+ rather than the message.
323
+
324
+ Flagged: template literals with expressions, and `+` concatenation. A plain
325
+ identifier passes, so `logger.info(message)` is fine — chasing that would need
326
+ type information, which no rule in this plugin requires.
327
+
328
+ A call counts as logging when the method is a level (`log`, `info`, `warn`,
329
+ `error`, `debug`, `verbose`, `trace`, `fatal`) and the receiver is named `logger`
330
+ or `log`, whether local or a field (`this.logger.info`). Both lists are widened
331
+ with `{ objects: string[], methods: string[] }`:
332
+
333
+ ```js
334
+ 'elegant/no-interpolated-log-message': ['error', { objects: ['audit'] }],
335
+ ```
336
+
337
+ **Known limitation.** When the first argument is an identifier, it is taken for
338
+ the message, so pino's error form slips through:
339
+
340
+ ```ts
341
+ logger.error(err, `order ${id} failed`); // not reported
342
+ ```
343
+
344
+ Telling that apart from `logger.info(message)` needs type information. The case
345
+ is pinned by a test so the behaviour is deliberate rather than accidental.
346
+
347
+ #### `max-returns`
348
+
349
+ A port of Checkstyle's `ReturnCount`, but not of its threshold. qulice sets it
350
+ to `1` — a single exit — which reads well in Java and badly here, because it
351
+ outlaws the guard clause that `no-else-after-throw` in this very preset pushes
352
+ you towards. Two rules in one config should not disagree.
353
+
354
+ At `max: 3` the rule stops arguing about single exit and measures sprawl
355
+ instead. Two or three guards followed by a final `return` pass; a function
356
+ leaving from six different places is the one worth splitting.
357
+
358
+ ```ts
359
+ // passes — idiomatic guards
360
+ function charge(amount: number): number {
361
+ if (amount < 0) return 0;
362
+ if (amount > limit) return limit;
363
+ return amount;
364
+ }
365
+ ```
366
+
367
+ Every function gets its own budget, so a callback's exits are never charged to
368
+ the function hosting it. Bare `return;` counts — leaving early is leaving,
369
+ value or not. An arrow with an expression body has no `return` statement at all
370
+ and never trips the rule.
371
+
372
+ Functions are reported by the name that binds them — a declaration's own, a
373
+ method's key, or the `const` or class field holding an arrow — falling back to
374
+ `(anonymous)` for an inline callback. Configurable via `{ max: number }`
375
+ (default `3`).
376
+
377
+ #### `no-property-alias`
378
+
379
+ A local whose whole job is to hold `obj.status` is a second name for state the
380
+ object already exposes under a name of its own. It buys nothing and it costs a
381
+ reader the hop of proving the two are the same value. Ask the object where you
382
+ need the answer.
383
+
384
+ ```ts
385
+ // reported
386
+ const objStatus = obj.status;
387
+ const authHeader = request.headers.authorization;
388
+ const region = this.cognitoRegion;
389
+
390
+ // passes
391
+ obj.status;
392
+ request.headers.authorization;
393
+ this.cognitoRegion;
394
+ ```
395
+
396
+ Only variable declarations are reported. `this.total = other.total` transfers
397
+ state rather than aliasing it, and a property in an object literal
398
+ (`{ id: dto.id }`) is how mappers are written; neither trips the rule.
399
+
400
+ Four shapes are never reported, because in each of them the local is doing real
401
+ work:
402
+
403
+ - **A reassigned local.** `let status = obj.status` followed by
404
+ `status = 'EXPIRED'` holds mutable state that no member access stands in for.
405
+ - **A local read inside a nested function.** TypeScript drops a narrowing of
406
+ `obj.prop` at the callback boundary but keeps it on a local, so inlining such
407
+ a declaration stops compiling:
408
+
409
+ ```ts
410
+ const status = obj.status;
411
+ if (status === undefined) return [];
412
+ return obj.items.map((n) => n + status.length); // needs the local
413
+ ```
414
+
415
+ - **A chain that is not a plain run of `.prop` accesses.** A computed link
416
+ (`repo.save.mock.calls[1][0].metadata`) or a call in the middle
417
+ (`resolveDates(query).startDate`) is not a property of an object in hand, and
418
+ repeating it reads worse than naming it.
419
+ - **An environment read.** `const topicArn = process.env.SNS_ERROR_TOPIC`
420
+ followed by a guard is fail-fast, and inlining it would read the environment
421
+ twice. Set `{ allowEnv: false }` to hold these to the same standard.
422
+
423
+ Its sibling `no-property-destructuring` covers the same reach-in written as a
424
+ pattern; together they say one thing, which is to ask the object.
425
+
426
+ This rule is the mirror image of ESLint's native
427
+ [`prefer-destructuring`](https://eslint.org/docs/latest/rules/prefer-destructuring),
428
+ which reports `const status = obj.status` and asks you to write
429
+ `const { status } = obj` instead. The two cannot both be on. `prefer-destructuring`
430
+ is off by default, so there is nothing to undo unless you enabled it — and note
431
+ that it only fires when the local and the property share a name, leaving the
432
+ renaming majority (`const objStatus = obj.status`) unreported either way.
433
+
434
+ #### `no-property-destructuring`
435
+
436
+ `const { status, enabled } = obj` is `no-property-alias` written as a pattern:
437
+ the object already names its own state, and the locals are a second set of
438
+ names for it. This rule covers the pattern form, and only when the thing being
439
+ destructured is an object you already hold — a name, `this`, or a run of plain
440
+ `.prop` accesses rooted at one of those.
441
+
442
+ ```ts
443
+ // reported
444
+ const { status, enabled } = obj;
445
+ const { access_token, expires_in } = response.data;
446
+ const { region, poolId } = this.config;
447
+
448
+ // passes — none of these was an object in hand
449
+ function create({ id, name }) {}
450
+ for (const { id, total } of rows) {}
451
+ const { csvContent } = await service.exportCsv(query);
452
+ const { startDate } = resolveDates(query);
453
+ const [rows, total] = await repo.findAndCount();
454
+ ```
455
+
456
+ Parameter patterns, loop bindings, and `catch` bindings are how you receive a
457
+ value rather than reach into one, so they never come up. Neither does
458
+ `ArrayPattern`: `const [rows, total] = ...` names the halves of a tuple that
459
+ carries no names of its own.
460
+
461
+ Four shapes are never reported, because in each of them the pattern is doing
462
+ work no member access does:
463
+
464
+ - **A rest element.** `const { authorization: _auth, ...safe } = headers`
465
+ constructs a new object by omission. There is nothing to inline it into.
466
+ - **A default value.** `const { max = 3 } = options` inlines to
467
+ `options.max ?? 3`, repeating the fallback at every use site.
468
+ - **A local read inside a nested function**, for the narrowing reason spelled
469
+ out under `no-property-alias`.
470
+ - **A local reassigned later.** `let { status } = obj` followed by
471
+ `status = 'EXPIRED'` holds mutable state of its own.
472
+
473
+ Renaming on the way out (`const { ingestion: failure } = row`) is still
474
+ copying, and so is a single property. Width makes no difference: a pattern
475
+ pulling four fields off an `input` is usually the sign that the method wanted
476
+ the object, not the fields.
477
+
478
+ #### `no-anonymous-param-type`
479
+
480
+ `max-params` and `no-boolean-param` both push you towards an options object —
481
+ and an options object typed inline is a bag that got away with it. The
482
+ parameter count went down, the coupling did not, and the shape has nowhere to
483
+ grow behaviour. Give it a name and it can become a value object; leave it
484
+ anonymous and it stays a struct.
485
+
486
+ ```ts
487
+ // reported
488
+ private toResponse(group: { id: string; name: string; members: number }) {}
489
+ async createFundingProducts(data: { originCode: string; productId: string }) {}
490
+ chart(rows: Array<{ day: string; count: string }>) {}
491
+ constructor(private readonly config: { host: string; port: number }) {}
492
+
493
+ // passes
494
+ async register(input: RegisterProposal) {}
495
+ function charge(amount: number, currency: string) {}
496
+ ingest(raw: Record<string, unknown>) {}
497
+ ```
498
+
499
+ A shape counts wherever it hides in the annotation — on its own, in a union
500
+ with `null`, intersected onto a named type, in an array, or inside a generic
501
+ argument such as `Array<{ … }>`. A parameter is reported once however many
502
+ shapes it holds, and each offending parameter is reported separately.
503
+ Destructuring in the signature (`function create({ id }: { id: string })`)
504
+ does not hide the bag, and neither does a default value.
505
+
506
+ **Inline callbacks are never reported.** `res.body.items.map((i: { ccbNumber: string; total: number }) => i.total)`
507
+ annotates whatever the callee yields; when that value has no type to borrow, an
508
+ inline shape is the only way to type it at all.
509
+
510
+ Configurable via `{ minMembers: number }` (default `2`). At the default, a
511
+ one-property parameter like `opts?: { required?: boolean }` passes — naming a
512
+ single field is usually ceremony rather than design. Set `minMembers: 1` to
513
+ hold those to the same standard.
514
+
515
+ Unlike its neighbours, this rule does not have a mechanical fix: it asks you to
516
+ introduce a named type and decide where it lives. That is a design change, so
517
+ expect adoption to cost more than a find-and-replace.
518
+
156
519
  ## Configuration
157
520
 
158
521
  ### Overriding thresholds
159
522
 
160
- `max-class-methods` and the native `max-params` both take a `max` option:
523
+ `max-class-methods`, `max-class-dependencies`, `max-class-fields`, and the
524
+ native `max-params` all take a `max` option:
161
525
 
162
526
  ```js
163
527
  rules: {
164
528
  ...elegant.configs.recommended.rules,
165
529
  'elegant/max-class-methods': ['warn', { max: 15 }],
530
+ 'elegant/max-class-dependencies': ['warn', { max: 6 }],
531
+ 'elegant/max-class-fields': ['warn', { max: 8 }],
166
532
  'max-params': ['warn', { max: 4 }],
167
533
  }
168
534
  ```
@@ -178,6 +544,9 @@ block scoped to your spec globs:
178
544
  rules: {
179
545
  'elegant/no-boolean-param': 'off',
180
546
  'elegant/max-class-methods': 'off',
547
+ 'elegant/max-class-dependencies': 'off',
548
+ 'elegant/max-class-fields': 'off',
549
+ 'elegant/no-comments-in-function-body': 'off',
181
550
  'max-params': 'off',
182
551
  },
183
552
  }
@@ -190,6 +559,19 @@ The package ships a single CommonJS build that is consumable as both
190
559
  `import elegant from '@tianjos/eslint-plugin-elegant'`. The exported object
191
560
  exposes `{ meta, rules, configs }`.
192
561
 
562
+ ## Prior art
563
+
564
+ This plugin is a TypeScript adaptation of [Elegant Objects](https://www.elegantobjects.org/)
565
+ (Yegor Bugayenko) and [qulice](https://github.com/yegor256/qulice) — the Java
566
+ quality enforcer that codifies those principles on top of Checkstyle and PMD.
567
+ Rules such as `no-logic-in-constructor` (qulice's `ConstructorsCodeFreeCheck`),
568
+ `max-class-dependencies` (Checkstyle's `ClassDataAbstractionCoupling`),
569
+ `max-class-fields` (PMD's `TooManyFields`),
570
+ `no-comments-in-function-body` (`MethodBodyCommentsCheck`), `no-null`,
571
+ `no-getters-setters`, and `no-static-members` are ports of that
572
+ philosophy. The concepts are reimplemented from scratch against the TypeScript
573
+ AST; no qulice code is used.
574
+
193
575
  ## License
194
576
 
195
577
  [MIT](./LICENSE) © Thiago
package/dist/index.d.ts CHANGED
@@ -8,6 +8,18 @@ declare const rules: {
8
8
  }], unknown, TSESLint.RuleListener> & {
9
9
  name: string;
10
10
  };
11
+ 'max-class-dependencies': TSESLint.RuleModule<"tooManyDependencies", [{
12
+ max: number;
13
+ ignore: string[];
14
+ }], unknown, TSESLint.RuleListener> & {
15
+ name: string;
16
+ };
17
+ 'max-class-fields': TSESLint.RuleModule<"tooManyFields", [{
18
+ max: number;
19
+ ignoreDecorated: boolean;
20
+ }], unknown, TSESLint.RuleListener> & {
21
+ name: string;
22
+ };
11
23
  'no-type-assertion': TSESLint.RuleModule<"noAssertion", [], unknown, TSESLint.RuleListener> & {
12
24
  name: string;
13
25
  };
@@ -36,6 +48,38 @@ declare const rules: {
36
48
  'no-null': TSESLint.RuleModule<"noNull", [], unknown, TSESLint.RuleListener> & {
37
49
  name: string;
38
50
  };
51
+ 'no-comments-in-function-body': TSESLint.RuleModule<"commentInBody", [{
52
+ allow: string[];
53
+ }], unknown, TSESLint.RuleListener> & {
54
+ name: string;
55
+ };
56
+ 'no-else-after-throw': TSESLint.RuleModule<"elseAfterThrow", [], unknown, TSESLint.RuleListener> & {
57
+ name: string;
58
+ };
59
+ 'no-interpolated-log-message': TSESLint.RuleModule<"interpolatedMessage", [{
60
+ objects: string[];
61
+ methods: string[];
62
+ }], unknown, TSESLint.RuleListener> & {
63
+ name: string;
64
+ };
65
+ 'max-returns': TSESLint.RuleModule<"tooManyReturns", [{
66
+ max: number;
67
+ }], unknown, TSESLint.RuleListener> & {
68
+ name: string;
69
+ };
70
+ 'no-property-alias': TSESLint.RuleModule<"aliasesProperty", [{
71
+ allowEnv: boolean;
72
+ }], unknown, TSESLint.RuleListener> & {
73
+ name: string;
74
+ };
75
+ 'no-property-destructuring': TSESLint.RuleModule<"destructuresObject", [], unknown, TSESLint.RuleListener> & {
76
+ name: string;
77
+ };
78
+ 'no-anonymous-param-type': TSESLint.RuleModule<"anonymousParamType", [{
79
+ minMembers: number;
80
+ }], unknown, TSESLint.RuleListener> & {
81
+ name: string;
82
+ };
39
83
  };
40
84
  type Plugin = {
41
85
  meta: {
package/dist/index.js CHANGED
@@ -2,20 +2,35 @@
2
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
+ const no_anonymous_param_type_1 = __importDefault(require("./rules/no-anonymous-param-type"));
6
+ const max_class_dependencies_1 = __importDefault(require("./rules/max-class-dependencies"));
7
+ const max_class_fields_1 = __importDefault(require("./rules/max-class-fields"));
8
+ const max_returns_1 = __importDefault(require("./rules/max-returns"));
5
9
  const max_class_methods_1 = __importDefault(require("./rules/max-class-methods"));
6
10
  const no_boolean_param_1 = __importDefault(require("./rules/no-boolean-param"));
11
+ const no_comments_in_function_body_1 = __importDefault(require("./rules/no-comments-in-function-body"));
12
+ const no_else_after_throw_1 = __importDefault(require("./rules/no-else-after-throw"));
7
13
  const no_getters_setters_1 = __importDefault(require("./rules/no-getters-setters"));
8
14
  const no_instanceof_1 = __importDefault(require("./rules/no-instanceof"));
15
+ const no_interpolated_log_message_1 = __importDefault(require("./rules/no-interpolated-log-message"));
9
16
  const no_logic_in_constructor_1 = __importDefault(require("./rules/no-logic-in-constructor"));
10
17
  const no_null_1 = __importDefault(require("./rules/no-null"));
11
18
  const no_null_return_1 = __importDefault(require("./rules/no-null-return"));
19
+ const no_property_alias_1 = __importDefault(require("./rules/no-property-alias"));
20
+ const no_property_destructuring_1 = __importDefault(require("./rules/no-property-destructuring"));
12
21
  const no_public_mutable_props_1 = __importDefault(require("./rules/no-public-mutable-props"));
13
22
  const no_static_members_1 = __importDefault(require("./rules/no-static-members"));
14
23
  const no_type_assertion_1 = __importDefault(require("./rules/no-type-assertion"));
24
+ // require() of a JSON file yields `any`, so there is no honest type to reach
25
+ // for here. Importing it instead would put package.json inside the emitted
26
+ // tree and move dist/index.js, which the "exports" map pins.
27
+ // eslint-disable-next-line elegant/no-type-assertion
15
28
  const { name, version } = require('../package.json');
16
29
  const rules = {
17
30
  'no-boolean-param': no_boolean_param_1.default,
18
31
  'max-class-methods': max_class_methods_1.default,
32
+ 'max-class-dependencies': max_class_dependencies_1.default,
33
+ 'max-class-fields': max_class_fields_1.default,
19
34
  'no-type-assertion': no_type_assertion_1.default,
20
35
  'no-null-return': no_null_return_1.default,
21
36
  'no-public-mutable-props': no_public_mutable_props_1.default,
@@ -24,6 +39,13 @@ const rules = {
24
39
  'no-instanceof': no_instanceof_1.default,
25
40
  'no-static-members': no_static_members_1.default,
26
41
  'no-null': no_null_1.default,
42
+ 'no-comments-in-function-body': no_comments_in_function_body_1.default,
43
+ 'no-else-after-throw': no_else_after_throw_1.default,
44
+ 'no-interpolated-log-message': no_interpolated_log_message_1.default,
45
+ 'max-returns': max_returns_1.default,
46
+ 'no-property-alias': no_property_alias_1.default,
47
+ 'no-property-destructuring': no_property_destructuring_1.default,
48
+ 'no-anonymous-param-type': no_anonymous_param_type_1.default,
27
49
  };
28
50
  const plugin = {
29
51
  meta: { name, version },
@@ -36,6 +58,9 @@ plugin.configs.recommended = {
36
58
  rules: {
37
59
  'elegant/no-boolean-param': 'error',
38
60
  'elegant/max-class-methods': ['warn', { max: 10 }],
61
+ 'elegant/max-class-dependencies': ['warn', { max: 4 }],
62
+ 'elegant/max-class-fields': ['warn', { max: 5 }],
63
+ 'elegant/max-returns': ['warn', { max: 3 }],
39
64
  'elegant/no-type-assertion': 'error',
40
65
  'elegant/no-null-return': 'error',
41
66
  'elegant/no-public-mutable-props': 'error',
@@ -44,7 +69,14 @@ plugin.configs.recommended = {
44
69
  'elegant/no-instanceof': 'error',
45
70
  'elegant/no-static-members': 'error',
46
71
  'elegant/no-null': 'error',
72
+ 'elegant/no-comments-in-function-body': 'error',
73
+ 'elegant/no-else-after-throw': 'error',
74
+ 'elegant/no-interpolated-log-message': 'error',
75
+ 'elegant/no-property-alias': 'error',
76
+ 'elegant/no-property-destructuring': 'error',
77
+ 'elegant/no-anonymous-param-type': ['error', { minMembers: 2 }],
47
78
  'max-params': ['warn', { max: 3 }],
79
+ 'no-else-return': ['error', { allowElseIf: false }],
48
80
  },
49
81
  };
50
82
  plugin.default = plugin;
@@ -0,0 +1,9 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ type Options = [{
3
+ max: number;
4
+ ignore: string[];
5
+ }];
6
+ declare const _default: TSESLint.RuleModule<"tooManyDependencies", Options, unknown, TSESLint.RuleListener> & {
7
+ name: string;
8
+ };
9
+ export default _default;