@croct/eslint-plugin 0.7.1 → 0.8.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/configs/react.js CHANGED
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.react = void 0;
4
4
  exports.react = {
5
5
  extends: [
6
- 'airbnb',
7
6
  'plugin:react/recommended',
8
7
  'plugin:testing-library/react',
9
8
  'plugin:jest-dom/recommended',
@@ -18,11 +17,71 @@ exports.react = {
18
17
  'jest-dom',
19
18
  '@croct',
20
19
  ],
20
+ parserOptions: {
21
+ ecmaFeatures: {
22
+ jsx: true,
23
+ },
24
+ },
25
+ settings: {
26
+ 'import/resolver': {
27
+ node: {
28
+ extensions: ['.js', '.jsx', '.json'],
29
+ },
30
+ },
31
+ react: {
32
+ pragma: 'React',
33
+ version: 'detect',
34
+ },
35
+ propWrapperFunctions: [
36
+ 'forbidExtraProps',
37
+ 'exact',
38
+ 'Object.freeze',
39
+ ],
40
+ },
21
41
  rules: {
42
+ // Custom rule
22
43
  '@croct/jsx-attribute-spacing': 'error',
23
- 'react/destructuring-assignment': 'off',
24
- 'react/jsx-wrap-multilines': 'error',
44
+ // React rules (from airbnb, with customizations)
25
45
  'react/display-name': 'off',
46
+ 'react/forbid-prop-types': ['error', {
47
+ forbid: ['any', 'array', 'object'],
48
+ checkContextTypes: true,
49
+ checkChildContextTypes: true,
50
+ }],
51
+ 'react/forbid-dom-props': ['off', { forbid: [] }],
52
+ 'react/jsx-boolean-value': ['error', 'never', { always: [] }],
53
+ 'react/jsx-closing-bracket-location': ['error', 'line-aligned'],
54
+ 'react/jsx-closing-tag-location': 'error',
55
+ 'react/jsx-curly-spacing': ['error', 'never', { allowMultiline: true }],
56
+ 'react/jsx-handler-names': ['off', {
57
+ eventHandlerPrefix: 'handle',
58
+ eventHandlerPropPrefix: 'on',
59
+ }],
60
+ 'react/jsx-indent-props': 'off',
61
+ 'react/jsx-key': 'off',
62
+ 'react/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }],
63
+ 'react/jsx-no-bind': [
64
+ 'error',
65
+ {
66
+ allowArrowFunctions: true,
67
+ allowBind: false,
68
+ allowFunctions: true,
69
+ },
70
+ ],
71
+ 'react/jsx-no-duplicate-props': ['error', { ignoreCase: true }],
72
+ 'react/jsx-no-literals': ['off', { noStrings: true }],
73
+ 'react/jsx-no-undef': 'error',
74
+ 'react/jsx-pascal-case': ['error', {
75
+ allowAllCaps: true,
76
+ ignore: [],
77
+ }],
78
+ 'react/sort-prop-types': ['off', {
79
+ ignoreCase: true,
80
+ callbacksLast: false,
81
+ requiredFirst: false,
82
+ sortShapeProp: true,
83
+ }],
84
+ 'react/jsx-sort-prop-types': 'off',
26
85
  'react/jsx-sort-props': [
27
86
  'error',
28
87
  {
@@ -30,78 +89,230 @@ exports.react = {
30
89
  multiline: 'last',
31
90
  },
32
91
  ],
33
- 'react/jsx-newline': [
34
- 'error',
92
+ 'react/jsx-sort-default-props': ['off', {
93
+ ignoreCase: true,
94
+ }],
95
+ 'react/jsx-uses-react': 'off',
96
+ 'react/jsx-uses-vars': 'error',
97
+ 'react/no-danger': 'warn',
98
+ 'react/no-deprecated': ['error'],
99
+ 'react/no-did-mount-set-state': 'off',
100
+ 'react/no-did-update-set-state': 'error',
101
+ 'react/no-will-update-set-state': 'error',
102
+ 'react/no-direct-mutation-state': 'off',
103
+ 'react/no-is-mounted': 'error',
104
+ 'react/no-multi-comp': 'off',
105
+ 'react/no-set-state': 'off',
106
+ 'react/no-string-refs': 'error',
107
+ 'react/no-unknown-property': 'error',
108
+ 'react/prefer-es6-class': ['error', 'always'],
109
+ 'react/prefer-stateless-function': ['error', { ignorePureComponents: true }],
110
+ 'react/prop-types': 'off',
111
+ 'react/react-in-jsx-scope': 'off',
112
+ 'react/require-render-return': 'error',
113
+ 'react/self-closing-comp': 'error',
114
+ 'react/sort-comp': ['error', {
115
+ order: [
116
+ 'static-variables',
117
+ 'static-methods',
118
+ 'instance-variables',
119
+ 'lifecycle',
120
+ '/^handle.+$/',
121
+ '/^on.+$/',
122
+ 'getters',
123
+ 'setters',
124
+ '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',
125
+ 'instance-methods',
126
+ 'everything-else',
127
+ 'rendering',
128
+ ],
129
+ groups: {
130
+ lifecycle: [
131
+ 'displayName',
132
+ 'propTypes',
133
+ 'contextTypes',
134
+ 'childContextTypes',
135
+ 'mixins',
136
+ 'statics',
137
+ 'defaultProps',
138
+ 'constructor',
139
+ 'getDefaultProps',
140
+ 'getInitialState',
141
+ 'state',
142
+ 'getChildContext',
143
+ 'getDerivedStateFromProps',
144
+ 'componentWillMount',
145
+ 'UNSAFE_componentWillMount',
146
+ 'componentDidMount',
147
+ 'componentWillReceiveProps',
148
+ 'UNSAFE_componentWillReceiveProps',
149
+ 'shouldComponentUpdate',
150
+ 'componentWillUpdate',
151
+ 'UNSAFE_componentWillUpdate',
152
+ 'getSnapshotBeforeUpdate',
153
+ 'componentDidUpdate',
154
+ 'componentDidCatch',
155
+ 'componentWillUnmount',
156
+ ],
157
+ rendering: [
158
+ '/^render.+$/',
159
+ 'render',
160
+ ],
161
+ },
162
+ }],
163
+ 'react/jsx-wrap-multilines': 'error',
164
+ 'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'],
165
+ 'react/jsx-equals-spacing': ['error', 'never'],
166
+ 'react/jsx-indent': ['error', 4],
167
+ 'react/jsx-no-target-blank': ['error', { enforceDynamicLinks: 'always' }],
168
+ 'react/jsx-filename-extension': [
169
+ 1,
35
170
  {
36
- prevent: true,
171
+ extensions: ['.tsx'],
37
172
  },
38
173
  ],
39
- 'react/jsx-no-bind': [
174
+ 'react/jsx-no-comment-textnodes': 'error',
175
+ 'react/no-render-return-value': 'error',
176
+ 'react/require-optimization': ['off', { allowDecorators: [] }],
177
+ 'react/no-find-dom-node': 'error',
178
+ 'react/forbid-component-props': ['off', { forbid: [] }],
179
+ 'react/forbid-elements': ['off', { forbid: [] }],
180
+ 'react/no-danger-with-children': 'error',
181
+ 'react/no-unused-prop-types': ['error', {
182
+ customValidators: [],
183
+ skipShapeProps: true,
184
+ }],
185
+ 'react/style-prop-object': 'error',
186
+ 'react/no-unescaped-entities': 'error',
187
+ 'react/no-children-prop': 'error',
188
+ 'react/jsx-tag-spacing': ['error', {
189
+ closingSlash: 'never',
190
+ beforeSelfClosing: 'always',
191
+ afterOpening: 'never',
192
+ beforeClosing: 'never',
193
+ }],
194
+ 'react/jsx-space-before-closing': ['off', 'always'],
195
+ 'react/no-array-index-key': 'error',
196
+ 'react/require-default-props': 'off',
197
+ 'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }],
198
+ 'react/void-dom-elements-no-children': 'error',
199
+ 'react/default-props-match-prop-types': ['error', { allowRequiredDefaults: false }],
200
+ 'react/no-redundant-should-component-update': 'error',
201
+ 'react/no-unused-state': 'error',
202
+ 'react/boolean-prop-naming': ['off', {
203
+ propTypeNames: ['bool', 'mutuallyExclusiveTrueProps'],
204
+ rule: '^(is|has)[A-Z]([A-Za-z0-9]?)+',
205
+ message: '',
206
+ }],
207
+ 'react/no-typos': 'error',
208
+ 'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }],
209
+ 'react/jsx-one-expression-per-line': 'off',
210
+ 'react/destructuring-assignment': 'off',
211
+ 'react/no-access-state-in-setstate': 'error',
212
+ 'react/button-has-type': ['error', {
213
+ button: true,
214
+ submit: true,
215
+ reset: false,
216
+ }],
217
+ 'react/jsx-child-element-spacing': 'off',
218
+ 'react/no-this-in-sfc': 'error',
219
+ 'react/jsx-max-depth': 'off',
220
+ 'react/jsx-props-no-multi-spaces': 'error',
221
+ 'react/no-unsafe': 'off',
222
+ 'react/jsx-fragments': ['error', 'element'],
223
+ 'react/jsx-curly-newline': ['error', {
224
+ multiline: 'consistent',
225
+ singleline: 'consistent',
226
+ }],
227
+ 'react/state-in-constructor': ['error', 'always'],
228
+ 'react/static-property-placement': ['error', 'property assignment'],
229
+ 'react/jsx-props-no-spreading': 'off',
230
+ 'react/prefer-read-only-props': 'off',
231
+ 'react/jsx-no-script-url': ['error', [
232
+ {
233
+ name: 'Link',
234
+ props: ['to'],
235
+ },
236
+ ]],
237
+ 'react/jsx-no-useless-fragment': [
40
238
  'error',
41
239
  {
42
- allowArrowFunctions: true,
43
- allowBind: false,
44
- allowFunctions: true,
240
+ allowExpressions: true,
45
241
  },
46
242
  ],
47
- 'react/no-unstable-nested-components': [
243
+ 'react/no-adjacent-inline-elements': 'off',
244
+ 'react/function-component-definition': 'off',
245
+ 'react/jsx-newline': [
48
246
  'error',
49
247
  {
50
- allowAsProps: true,
248
+ prevent: true,
51
249
  },
52
250
  ],
53
- 'react/jsx-no-useless-fragment': [
251
+ 'react/jsx-no-constructed-context-values': 'error',
252
+ 'react/no-unstable-nested-components': [
54
253
  'error',
55
254
  {
56
- allowExpressions: true,
255
+ allowAsProps: true,
57
256
  },
58
257
  ],
59
- 'react/function-component-definition': 'off',
258
+ 'react/no-namespace': 'error',
259
+ 'react/prefer-exact-props': 'error',
260
+ 'react/no-arrow-function-lifecycle': 'error',
261
+ 'react/no-invalid-html-attribute': 'error',
262
+ 'react/no-unused-class-component-methods': 'error',
263
+ // React Hooks rules
264
+ 'react-hooks/rules-of-hooks': 'error',
265
+ 'react-hooks/exhaustive-deps': 'warn',
266
+ // Testing Library rules (overrides)
60
267
  'testing-library/no-container': 'off',
61
268
  'testing-library/no-node-access': 'off',
62
269
  'testing-library/await-async-utils': 'off',
270
+ // JSX A11y rules (overrides)
63
271
  'jsx-a11y/aria-role': [
64
272
  'error',
65
273
  {
66
274
  ignoreNonDOM: true,
67
275
  },
68
276
  ],
69
- 'react/jsx-uses-react': 'off',
70
- 'react/react-in-jsx-scope': 'off',
71
- 'react/jsx-one-expression-per-line': 'off',
72
- 'react/prop-types': 'off',
73
- 'react/require-default-props': 'off',
74
- 'react/jsx-fragments': [
75
- 'error',
76
- 'element',
77
- ],
78
- 'react-hooks/rules-of-hooks': 'error',
79
- 'react-hooks/exhaustive-deps': 'warn',
80
- 'react/jsx-filename-extension': [
81
- 1,
82
- {
83
- extensions: [
84
- '.tsx',
277
+ // Underscore dangle override for Redux DevTools
278
+ 'no-underscore-dangle': ['error', {
279
+ allow: ['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'],
280
+ allowAfterThis: false,
281
+ allowAfterSuper: false,
282
+ enforceInMethodNames: true,
283
+ }],
284
+ // Class methods use this override for React lifecycle methods
285
+ 'class-methods-use-this': ['error', {
286
+ exceptMethods: [
287
+ 'render',
288
+ 'getInitialState',
289
+ 'getDefaultProps',
290
+ 'getChildContext',
291
+ 'componentWillMount',
292
+ 'UNSAFE_componentWillMount',
293
+ 'componentDidMount',
294
+ 'componentWillReceiveProps',
295
+ 'UNSAFE_componentWillReceiveProps',
296
+ 'shouldComponentUpdate',
297
+ 'componentWillUpdate',
298
+ 'UNSAFE_componentWillUpdate',
299
+ 'componentDidUpdate',
300
+ 'componentWillUnmount',
301
+ 'componentDidCatch',
302
+ 'getSnapshotBeforeUpdate',
85
303
  ],
86
- },
87
- ],
88
- 'react/jsx-indent': [
89
- 'error',
90
- 4,
91
- ],
92
- 'react/jsx-indent-props': 'off',
93
- 'react/jsx-props-no-spreading': 'off',
304
+ }],
305
+ // Import restrictions
94
306
  'no-restricted-imports': [
95
307
  'error',
96
308
  {
97
309
  name: 'react',
98
- importNames: [
99
- 'default',
100
- ],
310
+ importNames: ['default'],
101
311
  message: 'The React runtime is automatically imported, '
102
312
  + 'you can simply omit the import declaration in this case.',
103
313
  },
104
314
  ],
315
+ // Import order for React
105
316
  'import/order': [
106
317
  'error',
107
318
  {
@@ -117,9 +328,7 @@ exports.react = {
117
328
  position: 'before',
118
329
  },
119
330
  ],
120
- pathGroupsExcludedImportTypes: [
121
- 'react',
122
- ],
331
+ pathGroupsExcludedImportTypes: ['react'],
123
332
  'newlines-between': 'never',
124
333
  alphabetize: {
125
334
  order: 'asc',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croct/eslint-plugin",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "ESLint rules and presets applied to all Croct JavaScript projects.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -36,39 +36,37 @@
36
36
  "cypress"
37
37
  ],
38
38
  "dependencies": {
39
- "@rushstack/eslint-patch": "^1.1",
40
- "@typescript-eslint/eslint-plugin": "^6.4.0",
41
- "@typescript-eslint/utils": "^6.4.0",
42
- "eslint-config-airbnb": "^19.0",
43
- "eslint-config-airbnb-base": "^15.0",
44
- "eslint-plugin-cypress": "^2.12",
39
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
40
+ "@typescript-eslint/utils": "^8.53.0",
41
+ "eslint-plugin-cypress": "^5.2.1",
45
42
  "eslint-plugin-eslint-comments": "^3.2.0",
46
- "eslint-plugin-import": "^2.25",
47
- "eslint-plugin-import-newlines": "^1.1",
48
- "eslint-plugin-jest": "^27.0.0",
49
- "eslint-plugin-jest-dom": "^5.0.0",
50
- "eslint-plugin-jsx-a11y": "^6.5",
51
- "eslint-plugin-newline-destructuring": "^1.0.1",
52
- "eslint-plugin-no-smart-quotes": "^1.3",
53
- "eslint-plugin-react": "^7.28",
54
- "eslint-plugin-react-hooks": "^4.3",
55
- "eslint-plugin-testing-library": "^6.0.0"
43
+ "eslint-plugin-import": "^2.32.0",
44
+ "eslint-plugin-import-newlines": "^1.4.0",
45
+ "eslint-plugin-jest": "^29.12.1",
46
+ "eslint-plugin-jest-dom": "^5.5.0",
47
+ "eslint-plugin-jsx-a11y": "^6.10.2",
48
+ "eslint-plugin-newline-destructuring": "^1.2.2",
49
+ "eslint-plugin-no-smart-quotes": "^1.4.2",
50
+ "eslint-plugin-react": "^7.37.5",
51
+ "eslint-plugin-react-hooks": "^5.2.0",
52
+ "eslint-plugin-testing-library": "^7.15.4"
56
53
  },
57
54
  "devDependencies": {
58
- "@types/eslint": "^8.4",
59
- "@types/jest": "^29.0.0",
60
- "@types/semver": "^7.3.12",
61
- "@typescript-eslint/parser": "^6.4.0",
62
- "@typescript-eslint/types": "^6.4.0",
63
- "eslint": "^8.8",
64
- "eslint-plugin-eslint-plugin": "^5.0.0",
55
+ "@types/jest": "^29.5.14",
56
+ "@types/semver": "^7.5.8",
57
+ "@typescript-eslint/parser": "^8.53.0",
58
+ "@typescript-eslint/rule-tester": "^8.53.0",
59
+ "@typescript-eslint/types": "^8.53.0",
60
+ "@typescript-eslint/typescript-estree": "^8.53.0",
61
+ "eslint": "^9.28.0",
62
+ "eslint-plugin-eslint-plugin": "^6.4.0",
65
63
  "eslint-plugin-self": "^1.2.1",
66
- "jest": "^29.0.0",
67
- "ts-jest": "^29.0.0",
68
- "typescript": "^5.0.0"
64
+ "jest": "^29.7.0",
65
+ "ts-jest": "^29.3.4",
66
+ "typescript": "~5.8.3"
69
67
  },
70
68
  "peerDependencies": {
71
- "@typescript-eslint/parser": ">= 6",
72
- "eslint": ">= 8"
69
+ "@typescript-eslint/parser": ">= 8",
70
+ "eslint": ">= 9"
73
71
  }
74
72
  }
@@ -9,7 +9,6 @@ exports.argumentSpacing = (0, createRule_1.createRule)({
9
9
  docs: {
10
10
  description: 'Enforces a surrounding line break before and after '
11
11
  + 'the argument list in multiline functional calls.',
12
- recommended: 'recommended',
13
12
  },
14
13
  fixable: 'whitespace',
15
14
  schema: [],
@@ -8,7 +8,6 @@ exports.complexExpressionSpacing = (0, createRule_1.createRule)({
8
8
  type: 'suggestion',
9
9
  docs: {
10
10
  description: 'Enforces a surrounding line break in complex expression.',
11
- recommended: 'recommended',
12
11
  },
13
12
  fixable: 'whitespace',
14
13
  schema: [],
@@ -8,7 +8,6 @@ exports.jsxAttributeSpacing = (0, createRule_1.createRule)({
8
8
  type: 'suggestion',
9
9
  docs: {
10
10
  description: 'Enforces a surrounding line break in multiline JSX attributes.',
11
- recommended: 'recommended',
12
11
  },
13
12
  fixable: 'whitespace',
14
13
  schema: [],
@@ -21,7 +20,7 @@ exports.jsxAttributeSpacing = (0, createRule_1.createRule)({
21
20
  const sourceCode = context.getSourceCode();
22
21
  function check(node) {
23
22
  const { value } = node;
24
- if (value === null || value.type !== 'JSXExpressionContainer') {
23
+ if (value?.type !== 'JSXExpressionContainer') {
25
24
  return;
26
25
  }
27
26
  const firstToken = sourceCode.getFirstToken(value.expression);
@@ -10,7 +10,6 @@ exports.minChainedCallDepth = (0, createRule_1.createRule)({
10
10
  type: 'layout',
11
11
  docs: {
12
12
  description: 'Enforces a minimum depth for multiline chained calls.',
13
- recommended: 'stylistic',
14
13
  },
15
14
  fixable: 'whitespace',
16
15
  schema: [
@@ -39,9 +38,7 @@ exports.minChainedCallDepth = (0, createRule_1.createRule)({
39
38
  defaultOptions: [
40
39
  {
41
40
  maxLineLength: 100,
42
- },
43
- {
44
- ignoreChainDeeperThan: 3,
41
+ ignoreChainDeeperThan: 2,
45
42
  },
46
43
  ],
47
44
  create: context => {
@@ -90,10 +87,6 @@ exports.minChainedCallDepth = (0, createRule_1.createRule)({
90
87
  callee.type !== utils_1.AST_NODE_TYPES.MemberExpression
91
88
  // If the callee is a computed member expression, like `foo[bar]()`, skip.
92
89
  || callee.computed
93
- /* eslint-disable-next-line @typescript-eslint/ban-ts-comment --
94
- * NewExpression is a possible callee object type
95
- */
96
- // @ts-ignore
97
90
  || callee.object.type === utils_1.AST_NODE_TYPES.NewExpression
98
91
  // If the callee is already in the same line as it's object, skip.
99
92
  || callee.object.loc.end.line === callee.property.loc.start.line) {
@@ -9,7 +9,6 @@ exports.newlinePerChainedCall = (0, createRule_1.createRule)({
9
9
  type: 'layout',
10
10
  docs: {
11
11
  description: 'Require a newline after each call in a method chain',
12
- recommended: 'recommended',
13
12
  },
14
13
  fixable: 'whitespace',
15
14
  schema: [
@@ -9,7 +9,6 @@ exports.parameterDestructuring = (0, createRule_1.createRule)({
9
9
  type: 'layout',
10
10
  docs: {
11
11
  description: 'Prevent noisy destructuring on parameters',
12
- recommended: 'recommended',
13
12
  },
14
13
  hasSuggestions: true,
15
14
  schema: [],