@tianjos/eslint-plugin-elegant 0.2.0 → 0.3.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
@@ -81,6 +81,8 @@ The `recommended` config enables every custom rule plus the native
81
81
  | `elegant/no-logic-in-constructor` | custom | Any constructor code beyond `this.field = value` stores and a `super(...)` call | `error` |
82
82
  | `elegant/no-getters-setters` | custom | `get`/`set` accessors (and `getX`/`setX` methods with `{ methods: true }`) | `error` |
83
83
  | `elegant/no-instanceof` | custom | Use of the `instanceof` operator | `error` |
84
+ | `elegant/no-static-members` | custom | Static methods, properties, accessors, and blocks (`allowReadonly` to permit constants) | `error` |
85
+ | `elegant/no-null` | custom | The `null` literal as a value (type annotations and direct `return null` excepted) | `error` |
84
86
  | `max-params` | native | Functions declaring more than `max` parameters | `warn` (max 3) |
85
87
 
86
88
  ### Rule details
@@ -114,6 +116,18 @@ The `recommended` config enables every custom rule plus the native
114
116
  - **`no-instanceof`** — `instanceof` is type discrimination that belongs inside a
115
117
  polymorphic method on the object. Pairs with `no-type-assertion` to keep
116
118
  type-based branching out of the codebase.
119
+ - **`no-static-members`** — static state and behavior cannot be injected,
120
+ substituted, or mocked. Prefer instances (with dependency injection) and a
121
+ module-level `const` for shared values. The `{ allowReadonly: true }` option
122
+ permits `static readonly` constants. Note this also flags `static` factory
123
+ methods (`static create()`), which are common; relax per-file if your design
124
+ relies on them.
125
+ - **`no-null`** — completes `no-null-return` by banning the `null` literal as a
126
+ value everywhere (`const x = null`, `x === null`, `fn(null)`), pushing absence
127
+ into explicit types or `undefined`. `null` in type positions (`string | null`)
128
+ and a direct `return null` (owned by `no-null-return`) are left alone. This is
129
+ strict and will flag idioms like `JSON.stringify(x, null, 2)` — relax it in the
130
+ files where you interoperate with null-based APIs.
117
131
 
118
132
  ## Configuration
119
133
 
package/dist/index.d.ts CHANGED
@@ -28,6 +28,14 @@ declare const rules: {
28
28
  'no-instanceof': TSESLint.RuleModule<"noInstanceof", [], unknown, TSESLint.RuleListener> & {
29
29
  name: string;
30
30
  };
31
+ 'no-static-members': TSESLint.RuleModule<"staticMember", [{
32
+ allowReadonly: boolean;
33
+ }], unknown, TSESLint.RuleListener> & {
34
+ name: string;
35
+ };
36
+ 'no-null': TSESLint.RuleModule<"noNull", [], unknown, TSESLint.RuleListener> & {
37
+ name: string;
38
+ };
31
39
  };
32
40
  type Plugin = {
33
41
  meta: {
package/dist/index.js CHANGED
@@ -7,8 +7,10 @@ const no_boolean_param_1 = __importDefault(require("./rules/no-boolean-param"));
7
7
  const no_getters_setters_1 = __importDefault(require("./rules/no-getters-setters"));
8
8
  const no_instanceof_1 = __importDefault(require("./rules/no-instanceof"));
9
9
  const no_logic_in_constructor_1 = __importDefault(require("./rules/no-logic-in-constructor"));
10
+ const no_null_1 = __importDefault(require("./rules/no-null"));
10
11
  const no_null_return_1 = __importDefault(require("./rules/no-null-return"));
11
12
  const no_public_mutable_props_1 = __importDefault(require("./rules/no-public-mutable-props"));
13
+ const no_static_members_1 = __importDefault(require("./rules/no-static-members"));
12
14
  const no_type_assertion_1 = __importDefault(require("./rules/no-type-assertion"));
13
15
  const { name, version } = require('../package.json');
14
16
  const rules = {
@@ -20,6 +22,8 @@ const rules = {
20
22
  'no-logic-in-constructor': no_logic_in_constructor_1.default,
21
23
  'no-getters-setters': no_getters_setters_1.default,
22
24
  'no-instanceof': no_instanceof_1.default,
25
+ 'no-static-members': no_static_members_1.default,
26
+ 'no-null': no_null_1.default,
23
27
  };
24
28
  const plugin = {
25
29
  meta: { name, version },
@@ -38,6 +42,8 @@ plugin.configs.recommended = {
38
42
  'elegant/no-logic-in-constructor': 'error',
39
43
  'elegant/no-getters-setters': 'error',
40
44
  'elegant/no-instanceof': 'error',
45
+ 'elegant/no-static-members': 'error',
46
+ 'elegant/no-null': 'error',
41
47
  'max-params': ['warn', { max: 3 }],
42
48
  },
43
49
  };
@@ -0,0 +1,4 @@
1
+ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noNull", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ export default _default;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("@typescript-eslint/utils");
4
+ const createRule_1 = require("../utils/createRule");
5
+ exports.default = (0, createRule_1.createRule)({
6
+ name: 'no-null',
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Disallow the `null` literal as a value. Model absence with an explicit domain type, an Optional/Maybe, or by throwing. Null inside type annotations is allowed; direct `return null` is owned by `no-null-return`.',
11
+ },
12
+ messages: {
13
+ noNull: 'Avoid the null literal. Model absence explicitly instead of leaking null into the code.',
14
+ },
15
+ schema: [],
16
+ },
17
+ defaultOptions: [],
18
+ create(context) {
19
+ return {
20
+ Literal(node) {
21
+ if (node.value !== null || node.raw !== 'null') {
22
+ return;
23
+ }
24
+ // Direct `return null;` is the domain of `no-null-return`.
25
+ const { parent } = node;
26
+ if (parent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
27
+ parent.argument === node) {
28
+ return;
29
+ }
30
+ context.report({ node, messageId: 'noNull' });
31
+ },
32
+ };
33
+ },
34
+ });
@@ -0,0 +1,7 @@
1
+ type Options = [{
2
+ allowReadonly: boolean;
3
+ }];
4
+ declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"staticMember", Options, unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
5
+ name: string;
6
+ };
7
+ export default _default;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const createRule_1 = require("../utils/createRule");
4
+ exports.default = (0, createRule_1.createRule)({
5
+ name: 'no-static-members',
6
+ meta: {
7
+ type: 'suggestion',
8
+ docs: {
9
+ description: 'Disallow static members. Static state and behavior cannot be injected, substituted, or mocked; prefer instances and a module-level value when you need a constant.',
10
+ },
11
+ messages: {
12
+ staticMember: 'Avoid static members. Use an injectable instance, or a module-level constant for shared values.',
13
+ },
14
+ schema: [
15
+ {
16
+ type: 'object',
17
+ properties: { allowReadonly: { type: 'boolean' } },
18
+ additionalProperties: false,
19
+ },
20
+ ],
21
+ },
22
+ defaultOptions: [{ allowReadonly: false }],
23
+ create(context, [{ allowReadonly }]) {
24
+ const reportKey = (key) => {
25
+ context.report({ node: key, messageId: 'staticMember' });
26
+ };
27
+ return {
28
+ MethodDefinition(node) {
29
+ if (node.static) {
30
+ reportKey(node.key);
31
+ }
32
+ },
33
+ PropertyDefinition(node) {
34
+ if (node.static && !(allowReadonly && node.readonly)) {
35
+ reportKey(node.key);
36
+ }
37
+ },
38
+ AccessorProperty(node) {
39
+ if (node.static) {
40
+ reportKey(node.key);
41
+ }
42
+ },
43
+ StaticBlock(node) {
44
+ context.report({ node, messageId: 'staticMember' });
45
+ },
46
+ };
47
+ },
48
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tianjos/eslint-plugin-elegant",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Opinionated ESLint rules for elegant, behavior-rich TypeScript: no flag arguments, no type assertions, no null returns, no public mutable state, and small focused classes.",
5
5
  "keywords": [
6
6
  "eslint",