@dartess/eslint-plugin 0.9.0 → 0.11.2

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +7 -1
  3. package/dist/configs/mobx.js +1 -0
  4. package/dist/configs/react.js +1 -0
  5. package/dist/configs/recommended.js +17 -3
  6. package/dist/configs/vendor-rules/best-practices.d.ts +0 -1
  7. package/dist/configs/vendor-rules/best-practices.js +0 -3
  8. package/dist/index.js +4 -0
  9. package/dist/rules/imports-max-parent-depth.d.ts +3 -1
  10. package/dist/rules/jsx-no-cross-context-classes.d.ts +4 -2
  11. package/dist/rules/jsx-no-cross-context-classes.js +2 -5
  12. package/dist/rules/jsx-no-text-as-child.d.ts +7 -1
  13. package/dist/rules/jsx-no-text-as-child.js +1 -1
  14. package/dist/rules/mobx-no-action-bound.d.ts +5 -0
  15. package/dist/rules/mobx-no-action-bound.js +25 -0
  16. package/dist/rules/mobx-require-observer.d.ts +3 -1
  17. package/dist/rules/mobx-require-observer.js +1 -1
  18. package/dist/rules/mobx-strict-observable-components-declaration.d.ts +3 -1
  19. package/dist/rules/mobx-sync-action.d.ts +3 -1
  20. package/dist/rules/mobx-sync-autorun.d.ts +3 -1
  21. package/dist/rules/no-props-with-children-type.d.ts +9 -0
  22. package/dist/rules/no-props-with-children-type.js +45 -0
  23. package/dist/rules/stories-export-meta.d.ts +3 -1
  24. package/dist/rules/stories-export-typed.d.ts +3 -1
  25. package/dist/rules/ts-named-tuple-elements.d.ts +3 -1
  26. package/dist/rules/ts-named-tuple-elements.js +2 -1
  27. package/dist/rules/utils/exhaustiveCheck.d.ts +1 -0
  28. package/dist/rules/utils/exhaustiveCheck.js +11 -0
  29. package/docs/rules/imports-max-parent-depth.md +1 -1
  30. package/docs/rules/mobx-no-action-bound.md +40 -0
  31. package/docs/rules/no-props-with-children-type.md +29 -0
  32. package/package.json +21 -12
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  [//]: # (https://keepachangelog.com/en/1.1.0/)
4
4
 
5
+ ## [0.11.0] - 2026-03-27
6
+
7
+ - remove throwing errors from rules
8
+ - add `unicorn/numeric-separators-style` to `recommended` config
9
+ - replace `import-x/order` with `perfectionist/sort-imports`: now sort side-effects as first group and styles as last group; please, install `eslint-plugin-perfectionist`
10
+ - add rule `@dartess/no-props-with-children-type`: Disallow the use of PropsWithChildren utility type. Prefer explicit children prop typing instead.
11
+ - add `@dartess/no-props-with-children-type` to `react` config
12
+ - update deps
13
+
14
+ ## [0.10.0] - 2026-02-23
15
+
16
+ - add rule `@dartess/mobx-no-action-bound`
17
+ - add `@dartess/mobx-no-action-bound` to `mobx` config
18
+ - change `no-sequences` option `allowInParentheses` from `true` to `false`
19
+ - allow `mobx-require-observer` without settings because `mobx` can be used without hooks
20
+
5
21
  ## [0.9.0] - 2026-02-03
6
22
 
7
23
  - add `eslint-plugin-complete` to `recommended` config
package/README.md CHANGED
@@ -42,6 +42,7 @@ npm i -D eslint \
42
42
  eslint-plugin-complete \
43
43
  eslint-plugin-decorator-position \
44
44
  eslint-plugin-de-morgan \
45
+ eslint-plugin-perfectionist \
45
46
  typescript-eslint \
46
47
  @eslint-community/eslint-plugin-eslint-comments \
47
48
  @dartess/eslint-plugin
@@ -70,6 +71,7 @@ npm i -D eslint-plugin-storybook
70
71
  Edit or create `eslint.config.ts` (or `eslint.config.mts`). You probably have to install `jiti` for it.
71
72
 
72
73
  ```ts
74
+ import type { TSESLint } from '@typescript-eslint/utils';
73
75
  import dartessEslintPluginRecommended from '@dartess/eslint-plugin/recommended';
74
76
  import dartessEslintPluginRecommendedPostFormat from '@dartess/eslint-plugin/recommended-post-format';
75
77
 
@@ -110,7 +112,7 @@ export default [
110
112
  // @see `Fine Tuning` -> `Formatters` section below
111
113
 
112
114
  ...dartessEslintPluginRecommendedPostFormat,
113
- ]
115
+ ] satisfies TSESLint.FlatConfig.ConfigArray;
114
116
 
115
117
  ```
116
118
 
@@ -154,6 +156,8 @@ This plugin requires manual setup for you build tools.
154
156
 
155
157
  If you're using Mobx with legacy decorators, you have to enable rule `mobx/missing-make-observable` manually.
156
158
 
159
+ if you're using Mobx stores with your custom hooks, set `settings.mobx.storeHooks` [by docs](docs/rules/mobx-require-observer.md).
160
+
157
161
  ## Supported Rules
158
162
 
159
163
  Each rule has emojis denoting:
@@ -171,6 +175,7 @@ Each rule has emojis denoting:
171
175
  | **React** | _config: react_ | | | |
172
176
  | [jsx-no-text-as-child](docs/rules/jsx-no-text-as-child.md) | JSX elements should not have text without translation | | | |
173
177
  | [jsx-no-cross-context-classes](docs/rules/jsx-no-cross-context-classes.md) | Prevent mixing of outer and inner classes to avoid dependency on style order. | | | |
178
+ | [no-props-with-children-type](docs/rules/no-props-with-children-type.md) | Disallow the use of `PropsWithChildren` utility type in favor explicit types. | ✅ | | |
174
179
  | **Storybook** | _config: storybook_ | | | |
175
180
  | [stories-export-meta](docs/rules/stories-export-meta.md) | Storybook's Meta should be typed | ✅ | | |
176
181
  | [stories-export-typed](docs/rules/stories-export-typed.md) | Storybook's Stories should be typed | ✅ | | |
@@ -179,6 +184,7 @@ Each rule has emojis denoting:
179
184
  | [mobx-require-observer](docs/rules/mobx-require-observer.md) | Components using the stores must be wrapped in an `observer` | ✅ | 🔧 | |
180
185
  | [mobx-sync-autorun](docs/rules/mobx-sync-autorun.md) | Enforce synchronous autorun callback | ✅ | | |
181
186
  | [mobx-sync-action](docs/rules/mobx-sync-action.md) | Enforce synchronous actions | ✅ | | |
187
+ | [mobx-no-action-bound](docs/rules/mobx-no-action-bound.md) | Enforce using arrow functions for binging `this` to actions | ✅ | | |
182
188
 
183
189
  ## Code Reuse Policy
184
190
 
@@ -9,6 +9,7 @@ const config = [
9
9
  'mobx/missing-make-observable': 'off', // useless with modern decorators syntax. TODO check original plugin?
10
10
  '@dartess/mobx-strict-observable-components-declaration': 'error',
11
11
  '@dartess/mobx-require-observer': 'error',
12
+ '@dartess/mobx-no-action-bound': 'error',
12
13
  '@dartess/mobx-sync-autorun': 'error', // TODO implement it by types?
13
14
  '@dartess/mobx-sync-action': 'error', // TODO implement it by types?
14
15
  },
@@ -58,6 +58,7 @@ const config = [
58
58
  '@eslint-react/naming-convention/component-name': 'error',
59
59
  '@stylistic/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
60
60
  '@stylistic/jsx-self-closing-comp': 'error',
61
+ '@dartess/no-props-with-children-type': 'error',
61
62
  },
62
63
  },
63
64
  ];
@@ -11,6 +11,7 @@ import eslintCommentsPlugin from '@eslint-community/eslint-plugin-eslint-comment
11
11
  import eslintPluginDecoratorPosition from 'eslint-plugin-decorator-position';
12
12
  import eslintPluginDeMorgan from 'eslint-plugin-de-morgan';
13
13
  import esLintPluginComplete from 'eslint-plugin-complete';
14
+ import esLintPluginPerfectionist from 'eslint-plugin-perfectionist';
14
15
  import dartessPlugin from "../index.js";
15
16
  import vendorRulesBestPractices from "./vendor-rules/best-practices.js";
16
17
  import vendorRulesErrors from "./vendor-rules/errors.js";
@@ -54,7 +55,9 @@ const config = [
54
55
  plugins: {
55
56
  unicorn: eslintPluginUnicorn,
56
57
  '@dartess': dartessPlugin,
58
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- https://github.com/NullVoxPopuli/eslint-plugin-decorator-position/issues/778
57
59
  'decorator-position': eslintPluginDecoratorPosition,
60
+ perfectionist: esLintPluginPerfectionist,
58
61
  },
59
62
  languageOptions: {
60
63
  parser: tsEslint.parser,
@@ -87,12 +90,20 @@ const config = [
87
90
  ],
88
91
  'unicorn/no-useless-undefined': 'error',
89
92
  'unicorn/prefer-node-protocol': 'error',
93
+ 'unicorn/numeric-separators-style': 'error',
90
94
  '@dartess/imports-max-parent-depth': 'error',
91
- 'import-x/order': [
95
+ 'perfectionist/sort-imports': [
92
96
  'error',
93
97
  {
94
- groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
95
- 'newlines-between': 'always',
98
+ type: 'unsorted',
99
+ groups: [
100
+ 'side-effect',
101
+ ...['builtin', 'external', 'internal', 'parent', 'sibling', 'index'].map(group => [
102
+ `type-${group}`,
103
+ `value-${group}`,
104
+ ]),
105
+ 'style',
106
+ ],
96
107
  },
97
108
  ],
98
109
  'prefer-arrow-callback': [
@@ -110,6 +121,8 @@ const config = [
110
121
  methods: 'above',
111
122
  },
112
123
  ],
124
+ // totally disable comma operator
125
+ 'no-sequences': ['error', { allowInParentheses: false }],
113
126
  },
114
127
  },
115
128
  {
@@ -232,6 +245,7 @@ const config = [
232
245
  'complete/strict-undefined-functions': 'off', // prefer unicorn/no-useless-undefined
233
246
  'complete/require-break': 'off', // can be false-positive with TS7027
234
247
  'complete/no-void-return-type': 'off', // conflict with @typescript-eslint/explicit-module-boundary-types
248
+ 'complete/require-ascii': 'off', // absoule useless
235
249
  },
236
250
  },
237
251
  {
@@ -83,7 +83,6 @@ declare const rules: {
83
83
  'no-return-assign': ["error", string];
84
84
  'no-script-url': "error";
85
85
  'no-self-compare': "error";
86
- 'no-sequences': "error";
87
86
  'no-useless-concat': "error";
88
87
  'no-void': "error";
89
88
  'prefer-promise-reject-errors': ["error", {
@@ -165,9 +165,6 @@ const rules = {
165
165
  // disallow comparisons where both sides are exactly the same
166
166
  // https://eslint.org/docs/rules/no-self-compare
167
167
  'no-self-compare': 'error',
168
- // disallow use of comma operator
169
- // https://eslint.org/docs/rules/no-sequences
170
- 'no-sequences': 'error',
171
168
  // disallow useless string concatenation
172
169
  // https://eslint.org/docs/rules/no-useless-concat
173
170
  'no-useless-concat': 'error',
package/dist/index.js CHANGED
@@ -5,10 +5,12 @@ import ruleStoriesExportMeta from "./rules/stories-export-meta.js";
5
5
  import ruleStoriesExportTyped from "./rules/stories-export-typed.js";
6
6
  import ruleMobxStrictObservableComponentsDeclaration from "./rules/mobx-strict-observable-components-declaration.js";
7
7
  import ruleMobxRequireObserver from "./rules/mobx-require-observer.js";
8
+ import ruleMobxNoActionBound from "./rules/mobx-no-action-bound.js";
8
9
  import ruleImportsMaxParentDepth from "./rules/imports-max-parent-depth.js";
9
10
  import ruleTsNamedTupleElements from "./rules/ts-named-tuple-elements.js";
10
11
  import ruleMobxSyncAutorun from "./rules/mobx-sync-autorun.js";
11
12
  import ruleMobxSyncAction from "./rules/mobx-sync-action.js";
13
+ import ruleNoPropsWithChildrenType from "./rules/no-props-with-children-type.js";
12
14
  const plugin = {
13
15
  meta: {
14
16
  name: packageJson.name,
@@ -23,9 +25,11 @@ const plugin = {
23
25
  'mobx-sync-action': ruleMobxSyncAction,
24
26
  'mobx-sync-autorun': ruleMobxSyncAutorun,
25
27
  'mobx-require-observer': ruleMobxRequireObserver,
28
+ 'mobx-no-action-bound': ruleMobxNoActionBound,
26
29
  'stories-export-typed': ruleStoriesExportTyped,
27
30
  'stories-export-meta': ruleStoriesExportMeta,
28
31
  'ts-named-tuple-elements': ruleTsNamedTupleElements,
32
+ 'no-props-with-children-type': ruleNoPropsWithChildrenType,
29
33
  },
30
34
  };
31
35
  export default plugin;
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"tooDeep", [], unknown, ESLintUtils.RuleListener>;
2
+ declare const _default: ESLintUtils.RuleModule<"tooDeep", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
3
5
  export default _default;
@@ -5,9 +5,11 @@ import { ESLintUtils } from '@typescript-eslint/utils';
5
5
  */
6
6
  type Options = [
7
7
  mainOptions: {
8
- libName?: string;
8
+ libName: string;
9
9
  }
10
10
  ];
11
11
  type MessageIds = 'avoidMix' | 'avoidRenaming';
12
- declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
12
+ declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
13
+ name: string;
14
+ };
13
15
  export default _default;
@@ -1,7 +1,7 @@
1
1
  import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
2
2
  export default ESLintUtils.RuleCreator(() => '')({
3
3
  name: 'jsx-no-cross-context-classes',
4
- defaultOptions: [{}],
4
+ defaultOptions: [{ libName: 'clsx' }],
5
5
  meta: {
6
6
  type: 'suggestion',
7
7
  docs: {
@@ -26,10 +26,7 @@ export default ESLintUtils.RuleCreator(() => '')({
26
26
  },
27
27
  create(context) {
28
28
  const [options] = context.options;
29
- const libName = options?.libName;
30
- if (!libName || typeof libName !== 'string') {
31
- throw new Error('libName option is required and must be a string');
32
- }
29
+ const { libName } = options;
33
30
  const isClassLike = (string) => /class/i.exec(string);
34
31
  return {
35
32
  CallExpression(node) {
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @fileoverview Disallows text as child in JSX elements, except specified characters like emojis, digits, special symbols and extra strings
3
+ * @author Sergey Kozlov
4
+ */
1
5
  import { ESLintUtils } from '@typescript-eslint/utils';
2
6
  type Options = [
3
7
  mainOptions: {
@@ -9,5 +13,7 @@ type Options = [
9
13
  }
10
14
  ];
11
15
  type MessageIds = 'textAsChild' | 'disallowedSymbols';
12
- declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
16
+ declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
17
+ name: string;
18
+ };
13
19
  export default _default;
@@ -1,8 +1,8 @@
1
- import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
2
1
  /**
3
2
  * @fileoverview Disallows text as child in JSX elements, except specified characters like emojis, digits, special symbols and extra strings
4
3
  * @author Sergey Kozlov
5
4
  */
5
+ import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
6
6
  import emojiRegex from 'emoji-regex';
7
7
  export default ESLintUtils.RuleCreator(() => '')({
8
8
  name: 'jsx-no-text-as-child',
@@ -0,0 +1,5 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ declare const _default: ESLintUtils.RuleModule<"noActionBound", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
5
+ export default _default;
@@ -0,0 +1,25 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export default ESLintUtils.RuleCreator(() => '')({
3
+ name: 'mobx-no-action-bound',
4
+ defaultOptions: [],
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Enforce using arrow functions for binging `this` to actions',
9
+ },
10
+ messages: {
11
+ noActionBound: 'rewrite action with arrow function for binding `this`',
12
+ },
13
+ schema: [],
14
+ },
15
+ create(context) {
16
+ return {
17
+ "Decorator[expression.object.name='action'][expression.property.name='bound']": function (node) {
18
+ context.report({
19
+ node,
20
+ messageId: 'noActionBound',
21
+ });
22
+ },
23
+ };
24
+ },
25
+ });
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"requireObserver", [], unknown, ESLintUtils.RuleListener>;
2
+ declare const _default: ESLintUtils.RuleModule<"requireObserver", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
3
5
  export default _default;
@@ -54,7 +54,7 @@ export default ESLintUtils.RuleCreator(() => '')({
54
54
  const mobxSettings = context.settings.mobx;
55
55
  const hooks = Array.isArray(mobxSettings?.storeHooks) ? mobxSettings.storeHooks : [];
56
56
  if (hooks.length === 0) {
57
- throw new Error('Please fill settings.mobx.storeHooks');
57
+ return {};
58
58
  }
59
59
  const { sourceCode } = context;
60
60
  const program = sourceCode.ast;
@@ -10,5 +10,7 @@ type Options = [
10
10
  }
11
11
  ];
12
12
  type MessageIds = 'observedIsInlined' | 'observedIsFE' | 'extraCallsAsParam' | 'observedIsNamed' | 'componentIsNamed' | 'componentIsNamedAsObserved';
13
- declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
13
+ declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
14
+ name: string;
15
+ };
14
16
  export default _default;
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"requireSyncAction", [], unknown, ESLintUtils.RuleListener>;
2
+ declare const _default: ESLintUtils.RuleModule<"requireSyncAction", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
3
5
  export default _default;
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"requireSyncAutorun", [], unknown, ESLintUtils.RuleListener>;
2
+ declare const _default: ESLintUtils.RuleModule<"requireSyncAutorun", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
3
5
  export default _default;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview Disallow the use of `PropsWithChildren` utility type in favor explicit types
3
+ * @author Sergey Kozlov
4
+ */
5
+ import { ESLintUtils } from '@typescript-eslint/utils';
6
+ declare const _default: ESLintUtils.RuleModule<"unexpectedPropsWithChildren", [], unknown, ESLintUtils.RuleListener> & {
7
+ name: string;
8
+ };
9
+ export default _default;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fileoverview Disallow the use of `PropsWithChildren` utility type in favor explicit types
3
+ * @author Sergey Kozlov
4
+ */
5
+ import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';
6
+ import { exhaustiveCheck } from "./utils/exhaustiveCheck.js";
7
+ function getTypeName(tsEntityName) {
8
+ switch (tsEntityName.type) {
9
+ case AST_NODE_TYPES.Identifier:
10
+ return tsEntityName.name;
11
+ case AST_NODE_TYPES.TSQualifiedName:
12
+ return tsEntityName.right.name;
13
+ case AST_NODE_TYPES.ThisExpression:
14
+ return null;
15
+ default:
16
+ exhaustiveCheck(tsEntityName);
17
+ }
18
+ }
19
+ export default ESLintUtils.RuleCreator(() => '')({
20
+ name: 'no-props-with-children-type',
21
+ defaultOptions: [],
22
+ meta: {
23
+ type: 'problem',
24
+ docs: {
25
+ description: 'Disallow the use of PropsWithChildren utility type. Prefer explicit children prop typing instead.',
26
+ },
27
+ messages: {
28
+ unexpectedPropsWithChildren: 'Do not use PropsWithChildren. Type the children prop explicitly instead.',
29
+ },
30
+ schema: [],
31
+ },
32
+ create(context) {
33
+ return {
34
+ TSTypeReference(node) {
35
+ const name = getTypeName(node.typeName);
36
+ if (name === 'PropsWithChildren') {
37
+ context.report({
38
+ node,
39
+ messageId: 'unexpectedPropsWithChildren',
40
+ });
41
+ }
42
+ },
43
+ };
44
+ },
45
+ });
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"exportDefaultTypeCheck", [], unknown, ESLintUtils.RuleListener>;
2
+ declare const _default: ESLintUtils.RuleModule<"exportDefaultTypeCheck", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
3
5
  export default _default;
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
- declare const _default: ESLintUtils.RuleModule<"exportStoryType", [], unknown, ESLintUtils.RuleListener>;
2
+ declare const _default: ESLintUtils.RuleModule<"exportStoryType", [], unknown, ESLintUtils.RuleListener> & {
3
+ name: string;
4
+ };
3
5
  export default _default;
@@ -9,5 +9,7 @@ type Options = [
9
9
  }
10
10
  ];
11
11
  type MessageIds = 'requireNames' | 'forbidNames';
12
- declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener>;
12
+ declare const _default: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
13
+ name: string;
14
+ };
13
15
  export default _default;
@@ -1,4 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { exhaustiveCheck } from "./utils/exhaustiveCheck.js";
2
3
  export default ESLintUtils.RuleCreator(() => '')({
3
4
  name: 'ts-named-tuple-elements',
4
5
  defaultOptions: [{ mode: 'always' }],
@@ -41,7 +42,7 @@ export default ESLintUtils.RuleCreator(() => '')({
41
42
  },
42
43
  };
43
44
  default:
44
- throw new Error(`Invalig mode value: ${String(mode)}`);
45
+ exhaustiveCheck(mode);
45
46
  }
46
47
  },
47
48
  });
@@ -0,0 +1 @@
1
+ export declare function exhaustiveCheck(param: never): never;
@@ -0,0 +1,11 @@
1
+ export function exhaustiveCheck(param) {
2
+ const paramAsString = (() => {
3
+ try {
4
+ return JSON.stringify(param);
5
+ }
6
+ catch {
7
+ return String(param);
8
+ }
9
+ })();
10
+ throw new Error(`unknown param: ${paramAsString}`);
11
+ }
@@ -20,7 +20,7 @@ import a from '../../two-up';
20
20
 
21
21
  ## Options
22
22
 
23
- This rule does not accept inline options, but requires a global ESLint setting:
23
+ This rule does not accept inline options, but allows a global ESLint setting:
24
24
 
25
25
  ```json
26
26
  {
@@ -0,0 +1,40 @@
1
+ # Enforce using arrow functions for binging `this` to actions (mobx-no-action-bound)
2
+
3
+ Decorator `@action.bound` automatically bind methods to class instances to avoid context loss (`this`).
4
+ However, this can also be achieved by declaring methods as an properies with arrow functions.
5
+
6
+ This is the first problem with bound methods—their functionality is duplicated by native abilities.
7
+
8
+ In general, for optimization purposes, it's not recommended to bind _everything_; by default,
9
+ class methods should be on the prototype.
10
+
11
+ To resolve the dilemma of "how to avoid binding unnecessary things and avoid losing context"
12
+ the [`@typescript-eslint/unbound-method`](https://typescript-eslint.io/rules/unbound-method/) rule exists.
13
+
14
+ However, it can't detect whether a method is bound via an `@action.bound`. Therefore, it's best to avoid
15
+ using `@action.bound` at all and leave only one binding method—arrow functions with `@typescript-eslint/unbound-method`.
16
+ In this case you don't need think about it.
17
+
18
+ ## Rule Details
19
+
20
+ Examples of **incorrect** code for this rule:
21
+
22
+ ```ts
23
+ class Store {
24
+ @action.bound
25
+ reset() {
26
+ this.value = null;
27
+ };
28
+ }
29
+ ```
30
+
31
+ Examples of **correct** code for this rule:
32
+
33
+ ```ts
34
+ class Store {
35
+ @action
36
+ reset = () => {
37
+ this.value = null;
38
+ };
39
+ }
40
+ ```
@@ -0,0 +1,29 @@
1
+ # Disallow `PropsWithChildren` utility type (no-props-with-children-type)
2
+
3
+ Disallow the use of `PropsWithChildren` utility type from `@types/react`.
4
+ Prefer explicit `children` prop typing instead, as `PropsWithChildren` makes `children` optional by default,
5
+ which may hide bugs. Additionally, `children` is often narrower than `ReactNode` and should be typed explicitly.
6
+
7
+ ## Rule Details
8
+
9
+ Examples of **incorrect** code for this rule:
10
+
11
+ ```ts
12
+ type ModalProps = PropsWithChildren<{ title: string }>;
13
+ ```
14
+
15
+ Examples of **correct** code for this rule:
16
+
17
+ ```ts
18
+ type ModalProps = {
19
+ title: string;
20
+ children: React.ReactNode;
21
+ };
22
+ ```
23
+
24
+ ```ts
25
+ type ModalProps = {
26
+ title: string;
27
+ children: string | number;
28
+ };
29
+ ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dartess/eslint-plugin",
3
3
  "type": "module",
4
- "version": "0.9.0",
4
+ "version": "0.11.2",
5
5
  "description": "A set of rules and configs for various TypeScript projects",
6
6
  "keywords": [
7
7
  "eslint",
@@ -59,6 +59,7 @@
59
59
  "eslint-plugin-import-x": "^4.1.0",
60
60
  "eslint-plugin-jsx-a11y": "^6.10.0",
61
61
  "eslint-plugin-mobx": "~0.0.13",
62
+ "eslint-plugin-perfectionist": "^5.6.0",
62
63
  "eslint-plugin-react-hooks": "^7.0.0",
63
64
  "eslint-plugin-storybook": "^10.0.0",
64
65
  "eslint-plugin-unicorn": ">=58.0.0",
@@ -88,32 +89,40 @@
88
89
  }
89
90
  },
90
91
  "devDependencies": {
91
- "@eslint-community/eslint-plugin-eslint-comments": "^4.6.0",
92
+ "@eslint-community/eslint-plugin-eslint-comments": "^4.7.1",
93
+ "@eslint-react/eslint-plugin": "^2.13.0",
92
94
  "@eslint/js": "^9.39.2",
95
+ "@next/eslint-plugin-next": "^16.1.6",
96
+ "@stylistic/eslint-plugin": "^5.10.0",
93
97
  "@types/confusing-browser-globals": "^1.0.3",
94
98
  "@types/eslint-plugin-jsx-a11y": "^6.10.1",
95
99
  "@types/eslint-plugin-mobx": "~0.0.0",
96
100
  "@types/node": "^25.0.2",
97
- "@typescript-eslint/rule-tester": "^8.50.0",
98
- "eslint": "^9.39.2",
101
+ "@typescript-eslint/rule-tester": "^8.56.1",
102
+ "eslint": "^9.39.4",
99
103
  "eslint-config-prettier": "^10.1.8",
100
104
  "eslint-import-resolver-typescript": "^4.4.4",
101
- "eslint-plugin-eslint-plugin": "^7.2.0",
105
+ "eslint-plugin-complete": "^1.3.0",
106
+ "eslint-plugin-de-morgan": "^2.1.1",
107
+ "eslint-plugin-decorator-position": "^6.0.0",
108
+ "eslint-plugin-eslint-plugin": "^7.3.2",
102
109
  "eslint-plugin-import-x": "^4.16.1",
103
- "eslint-plugin-prettier": "^5.5.4",
110
+ "eslint-plugin-perfectionist": "^5.6.0",
111
+ "eslint-plugin-prettier": "^5.5.5",
104
112
  "eslint-plugin-react-hooks": "^7.0.1",
105
- "eslint-plugin-unicorn": "^62.0.0",
113
+ "eslint-plugin-storybook": "^10.2.16",
114
+ "eslint-plugin-unicorn": "^63.0.0",
106
115
  "husky": "^9.1.7",
107
116
  "jiti": "^2.6.1",
108
- "rimraf": "^6.1.2",
117
+ "rimraf": "^6.1.3",
109
118
  "typescript": "~5.9.3",
110
- "typescript-eslint": "^8.50.0"
119
+ "typescript-eslint": "^8.56.1"
111
120
  },
112
121
  "dependencies": {
113
- "@eslint/compat": "^2.0.0",
114
- "@typescript-eslint/utils": "^8.50.0",
122
+ "@eslint/compat": "^2.0.3",
123
+ "@typescript-eslint/utils": "^8.56.1",
115
124
  "confusing-browser-globals": "^1.0.11",
116
125
  "emoji-regex": "^10.6.0",
117
- "globals": "^16.5.0"
126
+ "globals": "^17.4.0"
118
127
  }
119
128
  }