Skip to content

Commit 7f9c236

Browse files
authored
feat: drop prettier in favor of stylistic plugin (#926)
BREAKING CHANGE: dropped support of prettier
1 parent b557d0f commit 7f9c236

15 files changed

+370
-244
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ eslint configuration files used in @react-hookz projects
1313

1414
## Installation
1515

16-
This package does not install `eslint`, `prettier`, or `eslint-plugin-prettier`, so you need to
17-
install them manually.
16+
This package does not install `eslint`, so you need to install them manually.
1817

1918
```shell
20-
yarn add -D @react-hookz/eslint-config eslint eslint-plugin-prettier prettier
19+
yarn add -D @react-hookz/eslint-config eslint
2120
```
2221

2322
## Usage

configs/base.js

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import js from '@eslint/js';
2+
import stylistic from '@stylistic/eslint-plugin';
23
import xo from 'eslint-config-xo';
34
import importPlugin from 'eslint-plugin-import';
45
import eslintPluginNoUseExtendNative from 'eslint-plugin-no-use-extend-native';
5-
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
66
import pluginPromise from 'eslint-plugin-promise';
77
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
8+
import {setFilesIfUndef} from './util.js';
89

910
/** @typedef {import('eslint').Linter} Linter */
1011

@@ -101,17 +102,6 @@ export const importConfig = [
101102
},
102103
];
103104

104-
/**
105-
* Set configs file patters in case config does not have one.
106-
*
107-
* @param {Linter.Config[]} configs
108-
* @param {string[]} files
109-
* @return {Linter.Config[]}
110-
*/
111-
export function adjustESLintConfigFiles(configs, files) {
112-
return configs.map((cfg) => ({ files, ...cfg }));
113-
}
114-
115105
/** @type {Linter.Config[]} */
116106
const baseConfig = [
117107
js.configs.recommended,
@@ -252,10 +242,14 @@ const baseConfig = [
252242
// https://github.com/sindresorhus/eslint-plugin-unicorn/search?q=consistent-destructuring+is:issue&state=open&type=issues
253243
'unicorn/consistent-destructuring': 'off',
254244

255-
'unicorn/prefer-ternary': ['error', 'only-single-line'],
256-
257245
// It is up to app to decide file name casing.
258246
'unicorn/filename-case': 'off',
247+
248+
// null is completely okay IMO
249+
'unicorn/no-null': 'off',
250+
251+
// `if` statement should NOT be replaced by a ternary expression
252+
'unicorn/prefer-ternary': 'off',
259253
},
260254
},
261255

@@ -266,15 +260,30 @@ const baseConfig = [
266260
'default-case': 'off',
267261
'capitalized-comments': 'off',
268262
'function-call-argument-newline': 'off',
263+
},
264+
},
269265

270-
// conflicts with prettier
271-
'@stylistic/object-curly-spacing': 'off',
272-
'@stylistic/quotes': 'off',
273-
'@stylistic/arrow-parens': 'off',
274-
'@stylistic/comma-dangle': 'off',
266+
{
267+
plugins: {
268+
'@stylistic': stylistic,
269+
},
270+
rules: {
271+
'@stylistic/indent': ['error', 'tab', {tabLength: 2}],
272+
'@stylistic/max-len': [
273+
'warn',
274+
{
275+
code: 120,
276+
tabWidth: 2,
277+
ignoreComments: true,
278+
ignoreTrailingComments: true,
279+
ignoreUrls: true,
280+
ignoreStrings: true,
281+
ignoreTemplateLiterals: true,
282+
ignoreRegExpLiterals: true,
283+
},
284+
],
275285
},
276286
},
277-
eslintPluginPrettierRecommended,
278287
];
279288

280-
export default adjustESLintConfigFiles(baseConfig, ['**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}']);
289+
export default setFilesIfUndef(baseConfig, ['**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}']);

configs/jest.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import jest from 'eslint-plugin-jest';
2-
import { adjustESLintConfigFiles } from './base.js';
2+
import {setFilesIfUndef} from './util.js';
33

44
/** @type {Linter.Config[]} */
55
const jestConfig = [jest.configs['flat/recommended']];
6-
export default adjustESLintConfigFiles(jestConfig, ['**/*.{test,spec}.{js,jsx,mjs,cjs,ts,tsx}']);
6+
export default setFilesIfUndef(jestConfig, ['**/*.{test,spec}.{js,jsx,mjs,cjs,ts,tsx}']);

configs/md.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as mdx from 'eslint-plugin-mdx';
2-
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
32

43
/** @type {Linter.Config[]} */
54
const mdConfig = [
@@ -24,22 +23,6 @@ const mdConfig = [
2423
'import/no-anonymous-default-export': 'off',
2524
},
2625
},
27-
{
28-
...eslintPluginPrettierRecommended,
29-
files: ['**/*.{md,mdx}'],
30-
},
31-
{
32-
files: ['**/*.md'],
33-
rules: {
34-
'prettier/prettier': ['error', { parser: 'markdown' }],
35-
},
36-
},
37-
{
38-
files: ['**/*.mdx'],
39-
rules: {
40-
'prettier/prettier': ['error', { parser: 'mdx' }],
41-
},
42-
},
4326
];
4427

4528
export default mdConfig;

configs/node.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import nodePlugin from 'eslint-plugin-n';
2+
import {setFilesIfUndef} from './util.js';
23

34
/** @type {Linter.Config[]} */
45
const nodeConfig = [
56
nodePlugin.configs['flat/recommended-module'],
67
{
7-
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
88
languageOptions: {
99
ecmaVersion: 'latest',
1010
sourceType: 'module',
@@ -34,4 +34,4 @@ const nodeConfig = [
3434
},
3535
];
3636

37-
export default nodeConfig;
37+
export default setFilesIfUndef(nodeConfig, ['**/*.{js,mjs,cjs,ts,mts,cts}']);

configs/prettier.config.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

configs/react.js

Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pluginReact from 'eslint-plugin-react';
22
import pluginReactHooks from 'eslint-plugin-react-hooks';
3-
import { adjustESLintConfigFiles } from './base.js';
3+
import {setFilesIfUndef} from './util.js';
44

55
/** @var {Linter.Config[]} */
66
const reactConfig = [
@@ -18,6 +18,190 @@ const reactConfig = [
1818
},
1919
{
2020
rules: {
21+
'react/button-has-type': 'error',
22+
'react/jsx-child-element-spacing': 'error',
23+
'react/default-props-match-prop-types': 'error',
24+
'react/function-component-definition': [
25+
'error',
26+
{
27+
namedComponents: 'function-declaration',
28+
unnamedComponents: 'arrow-function',
29+
},
30+
],
31+
'react/hook-use-state': 'error',
32+
'react/iframe-missing-sandbox': 'error',
33+
'react/no-access-state-in-setstate': 'error',
34+
'react/no-array-index-key': 'error',
35+
'react/no-arrow-function-lifecycle': 'error',
36+
'react/no-children-prop': 'error',
37+
'react/no-danger': 'error',
38+
'react/no-danger-with-children': 'error',
39+
'react/no-deprecated': 'error',
40+
'react/no-did-update-set-state': 'error',
41+
'react/no-direct-mutation-state': 'error',
42+
'react/no-find-dom-node': 'error',
43+
'react/no-invalid-html-attribute': 'error',
44+
'react/no-is-mounted': 'error',
45+
'react/no-namespace': 'error',
46+
'react/no-redundant-should-component-update': 'error',
47+
'react/no-render-return-value': 'error',
48+
'react/no-typos': 'error',
49+
'react/no-string-refs': [
50+
'error',
51+
{
52+
noTemplateLiterals: true,
53+
},
54+
],
55+
'react/no-this-in-sfc': 'error',
56+
'react/no-unescaped-entities': 'error',
57+
'react/no-unknown-property': 'error',
58+
'react/no-unsafe': 'error',
59+
'react/no-unused-state': 'error',
60+
'react/prefer-read-only-props': 'error',
61+
'react/self-closing-comp': 'error',
62+
'react/state-in-constructor': [
63+
'error',
64+
'never',
65+
],
66+
'react/static-property-placement': 'error',
67+
'react/style-prop-object': [
68+
'error',
69+
{
70+
allow: [
71+
// This allows react-intl’s `<FormattedNumber value={0.42} style='percent'/>`.
72+
'FormattedNumber',
73+
],
74+
},
75+
],
76+
'react/void-dom-elements-no-children': 'error',
77+
'react/jsx-boolean-value': 'error',
78+
'react/jsx-closing-bracket-location': [
79+
'error',
80+
{
81+
nonEmpty: 'tag-aligned',
82+
selfClosing: false,
83+
},
84+
],
85+
'react/jsx-closing-tag-location': 'error',
86+
'react/jsx-curly-newline': [
87+
'error',
88+
{
89+
multiline: 'consistent',
90+
singleline: 'forbid',
91+
},
92+
],
93+
'react/jsx-curly-spacing': [
94+
'error',
95+
'never',
96+
],
97+
'react/jsx-equals-spacing': [
98+
'error',
99+
'never',
100+
],
101+
'react/jsx-first-prop-new-line': 'error',
102+
'react/jsx-indent': [
103+
'error',
104+
'tab',
105+
{
106+
checkAttributes: true,
107+
indentLogicalExpressions: true,
108+
},
109+
],
110+
'react/jsx-indent-props': [
111+
'error',
112+
'tab',
113+
],
114+
'react/jsx-key': [
115+
'error',
116+
{
117+
checkFragmentShorthand: true,
118+
checkKeyMustBeforeSpread: true,
119+
warnOnDuplicates: true,
120+
},
121+
],
122+
'react/jsx-max-props-per-line': [
123+
'error',
124+
{
125+
maximum: 3,
126+
when: 'multiline',
127+
},
128+
],
129+
'react/jsx-no-bind': [
130+
'error',
131+
{
132+
allowArrowFunctions: true,
133+
},
134+
],
135+
'react/jsx-no-comment-textnodes': 'error',
136+
'react/jsx-no-constructed-context-values': 'error',
137+
'react/jsx-no-duplicate-props': [
138+
'error',
139+
{
140+
ignoreCase: true,
141+
},
142+
],
143+
'react/jsx-no-script-url': 'error',
144+
'react/jsx-no-target-blank': [
145+
'error',
146+
{
147+
warnOnSpreadAttributes: true,
148+
forms: true,
149+
},
150+
],
151+
'react/jsx-no-undef': 'error',
152+
'react/jsx-no-useless-fragment': 'error',
153+
// Disabled for now as it produces too many errors
154+
// 'react/jsx-one-expression-per-line': ['error', {allow: 'single-child'}],
155+
'react/jsx-curly-brace-presence': [
156+
'error',
157+
{
158+
props: 'never',
159+
children: 'never',
160+
propElementValues: 'always',
161+
},
162+
],
163+
'react/jsx-fragments': [
164+
'error',
165+
'syntax',
166+
],
167+
'react/jsx-pascal-case': 'error',
168+
'react/jsx-props-no-multi-spaces': 'error',
169+
'react/jsx-sort-props': [
170+
'error',
171+
{
172+
callbacksLast: true,
173+
shorthandFirst: true,
174+
noSortAlphabetically: true,
175+
reservedFirst: true,
176+
},
177+
],
178+
'react/jsx-tag-spacing': [
179+
'error',
180+
{
181+
closingSlash: 'never',
182+
beforeSelfClosing: 'never',
183+
afterOpening: 'never',
184+
beforeClosing: 'never',
185+
},
186+
],
187+
'react/jsx-uses-react': 'error',
188+
'react/jsx-uses-vars': 'error',
189+
'react/jsx-wrap-multilines': [
190+
'error',
191+
{
192+
declaration: 'parens-new-line',
193+
assignment: 'parens-new-line',
194+
return: 'parens-new-line',
195+
arrow: 'parens-new-line',
196+
condition: 'ignore',
197+
logical: 'ignore',
198+
prop: 'ignore',
199+
},
200+
],
201+
202+
'react-hooks/rules-of-hooks': 'error',
203+
'react-hooks/exhaustive-deps': 'warn',
204+
21205
// While using ts with `react-jsx` preset - there
22206
// is no need in importing react in each file
23207
'react/react-in-jsx-scope': 'off',
@@ -33,4 +217,4 @@ const reactConfig = [
33217
},
34218
},
35219
];
36-
export default adjustESLintConfigFiles(reactConfig, ['**/*.{ts,tsx,js,jsx}']);
220+
export default setFilesIfUndef(reactConfig, ['**/*.{ts,tsx,js,jsx}']);

configs/typescript-unsafe.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { adjustESLintConfigFiles } from './base.js';
21
import typescriptConfig from './typescript.js';
2+
import {setFilesIfUndef} from './util.js';
33

44
/** @var {Linter.Config[]} */
55
const typescriptUnsafeConfig = [
@@ -16,4 +16,4 @@ const typescriptUnsafeConfig = [
1616
},
1717
},
1818
];
19-
export default adjustESLintConfigFiles(typescriptUnsafeConfig, ['**/*.{ts,tsx,mts,ctx}']);
19+
export default setFilesIfUndef(typescriptUnsafeConfig, ['**/*.{ts,tsx,mts,ctx}']);

0 commit comments

Comments
 (0)