npm package: @rccyx/eslint-plugin-classes
Description: Deterministic ES6 class discipline for humans and AI swarms
Author: @rccyx
Latest version: 1.1.1

# @rccyx/eslint-plugin-classes

Large teams and AI-generated code both create the same problem: classes slowly rot. Members drift out of order, private internals leak into public surfaces, and scanning a file gets exhaustive. This rule forces a strict, predictable layout so intent is never ambiguous. It stabilizes structure across humans and machines and prevents the entropy that usually builds up in big TypeScript codebases.

Enforce a readable class layout with explicit visibility, consistent naming, and a fixed member order.

## Install

```bash
pnpm i -D eslint @rccyx/eslint-plugin-classes
# or
bun add -d eslint @rccyx/eslint-plugin-classes
```

## Peer Dependencies

This plugin requires:

- `eslint`: `>=8.57 <11`
- `typescript`: `^5`

## Flat config usage

```js
// eslint.config.js
import classStructure from "@rccyx/eslint-plugin-classes";

export default [
  {
    files: ["**/*.ts", "**/*.tsx"],
    plugins: { "class-structure": classStructure },
    rules: {
      "class-structure/enforce": "error",
    },
  },
];
```

## What it enforces

### 1. Member ordering

Members must appear in this exact order:

1. Static fields
2. Static methods (including static accessors)
3. Instance fields (including method-like fields: `foo = () => {}`)
4. Constructor
5. Instance accessors (`get` / `set`)
6. Public instance methods
7. Protected instance methods
8. Private instance methods

```ts
// Good
class Example {
  static readonly VERSION = "1.0";
  static init() {}
  static get meta() {
    return "ok";
  }

  public id = "x";
  private _cache = new Map();

  constructor(id: string) {
    this.id = id;
  }

  get state() {}
  set state(v) {}

  public run() {}
  protected compute() {}
  private _cleanup() {}
}
```

### 2. Visibility requirements

All non-accessor instance methods and all method-like fields must explicitly declare visibility.

```ts
// Bad
class Bad {
  foo() {} // error
  handler = () => {}; // error
}

// Good
class Good {
  public foo() {}
  public handler = () => {};
}
```

**Exceptions:**

- Constructors may omit visibility
- Accessors may omit visibility
- Static members may omit visibility

### 3. Naming rules

#### Private members

Private fields and methods must start with `#` or `_`.

```ts
// Bad
private hidden() {}
private value: number = 1;

// Good
private _hidden() {}
private #value: number;
```

#### Public members

Public members must **not** start with `_` or `#`.

```ts
// Bad
public _call() {}
_id = 1;

// Good
public call() {}
id = 1;
```

**Note:** Accessors may start with `_` without error.

#### Constants

Static readonly fields must be SCREAMING_SNAKE_CASE.

```ts
// Bad
static readonly defaultLimit = 3;

// Good
static readonly MAX_LIMIT = 3;
private static readonly _CACHE_SIZE = 10;
```

### 4. Structural constraints

#### No fields after constructor

```ts
// Bad
class Bad {
  constructor() {}
  value = 1; // error
}
```

#### No static/instance interleaving

Static members of a category must all appear before instance members of that category.

```ts
// Bad
static A = 1;
a = 2;
static B = 3; // error
```

#### Class must have behavior

A class must declare at least one method, accessor, or method-like field.

```ts
// Bad
class Bad {
  value = 1; // no behavior
}
```

## What it flags

```ts
class Bad {
  value = 1;
  static init() {} // instance field before static method → error

  foo() {} // visibility missing
  private hidden() {} // private name must start with # or _
  public _call() {} // public name must not start with _ or #
  static readonly limit = 3; // constant naming error

  constructor() {}
  data = 2; // field after constructor → error

  public run() {}
  get name() {} // accessor after methods → error
}
```

## What passes

```ts
class Good {
  static readonly VERSION = "1.0";
  static init() {}
  static get meta() {
    return "ok";
  }

  public id = "x";
  protected mode = 0;
  private _cache = new Map<string, unknown>();
  public handler = () => {};

  constructor(id: string) {
    this.id = id;
  }

  get state() {
    return this.mode;
  }

  set state(v: number) {
    this.mode = v;
  }

  public run() {}
  protected compute() {}
  private _cleanup() {}
}
```

## Notes

- Method-like fields (`foo = () => {}`) behave like fields for ordering and methods for visibility.
- Static accessors behave like static methods for ordering.
- Accessors may start with `_` without violating public naming rules.
- Hash-private members (`#name`) automatically count as private.

## License

Apache-2.0 © [@rccyx](https://rccyx.com)