@itcase/lint 1.0.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.
@@ -0,0 +1,224 @@
1
+ import { fixupPluginRules } from '@eslint/compat'
2
+ import eslintJs from '@eslint/js'
3
+ import eslintPerfectionist from 'eslint-plugin-perfectionist'
4
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
5
+ import eslintReact from 'eslint-plugin-react'
6
+ import eslintReactHooks from 'eslint-plugin-react-hooks'
7
+ import eslintTypeScript from 'typescript-eslint'
8
+
9
+ const eslintStyleProps = [
10
+ 'width',
11
+ 'height',
12
+ 'minWidth',
13
+ 'minHeight',
14
+ 'maxWidth',
15
+ 'maxHeight',
16
+ 'aspectRatio',
17
+ 'font',
18
+ 'color',
19
+ 'labelTextColor',
20
+ 'labelTextSize',
21
+ 'fill',
22
+ 'fillHover',
23
+ 'background',
24
+ 'background-position',
25
+ 'background-repeat',
26
+ 'background-size',
27
+ 'padding',
28
+ 'margin',
29
+ 'border',
30
+ 'borderWidth',
31
+ 'borderColor',
32
+ 'borderStyle',
33
+ 'borderRadius',
34
+ 'borderLeft',
35
+ 'borderTop',
36
+ 'borderRight',
37
+ 'borderBottom',
38
+ 'float',
39
+ 'clear',
40
+ 'position',
41
+ 'overflow',
42
+ 'content',
43
+ 'display',
44
+ 'flex',
45
+ 'flexDirection',
46
+ 'flexWrap',
47
+ 'flexFlow',
48
+ 'order',
49
+ 'justify-content',
50
+ 'align-items',
51
+ 'align-self',
52
+ 'align-content',
53
+ 'flexGrow',
54
+ 'flexShrink',
55
+ 'flexBasis',
56
+ 'visibility',
57
+ 'box-sizing',
58
+ 'box-shadow',
59
+ 'left',
60
+ 'top',
61
+ 'right',
62
+ 'bottom',
63
+ 'verticalAlign',
64
+ 'zIndex',
65
+ 'transform',
66
+ 'transition',
67
+ 'opacity',
68
+ 'cursor',
69
+ ]
70
+
71
+ const eslintStylePropsCustomGroups = {}
72
+ eslintStyleProps.forEach((key) => (eslintStylePropsCustomGroups[key] = key))
73
+
74
+ const eslintConfig = [
75
+ eslintJs.configs.recommended,
76
+ eslintPluginPrettierRecommended,
77
+ ...eslintTypeScript.configs.recommended,
78
+ {
79
+ plugins: {
80
+ react: eslintReact,
81
+ 'react-hooks': fixupPluginRules(eslintReactHooks),
82
+ perfectionist: eslintPerfectionist,
83
+ },
84
+ },
85
+ {
86
+ ignores: ['dist', 'node_modules', 'eslint.config.mjs'],
87
+ },
88
+ {
89
+ files: ['**/*.{js,jsx,mjs,cjs,ts,tsx}'],
90
+ rules: {
91
+ ...eslintReactHooks.configs.recommended.rules,
92
+ camelcase: 'off',
93
+ 'multiline-ternary': 'off',
94
+ 'object-shorthand': ['warn', 'consistent'],
95
+
96
+ // @typescript-eslint
97
+ '@typescript-eslint/ban-ts-comment': 'off',
98
+ '@typescript-eslint/no-explicit-any': 'off',
99
+
100
+ // react
101
+ 'react/prop-types': 'error',
102
+ 'react/react-in-jsx-scope': 'off',
103
+
104
+ // react-hooks
105
+ 'react-hooks/exhaustive-deps': 'warn',
106
+ 'react-hooks/rules-of-hooks': 'error',
107
+
108
+ // Class
109
+ 'perfectionist/sort-classes': [
110
+ 'error',
111
+ {
112
+ type: 'natural',
113
+ order: 'asc',
114
+ groups: [
115
+ 'top',
116
+ 'index-signature',
117
+ 'static-property',
118
+ 'private-property',
119
+ 'property',
120
+ 'constructor',
121
+ 'static-method',
122
+ 'private-method',
123
+ 'get-method',
124
+ 'set-method',
125
+ 'method',
126
+ ],
127
+ customGroups: {
128
+ top: ['id'],
129
+ },
130
+ },
131
+ ],
132
+
133
+ // Objects
134
+ 'perfectionist/sort-objects': [
135
+ 'error',
136
+ {
137
+ type: 'natural',
138
+ order: 'asc',
139
+ groups: [
140
+ 'isMobile',
141
+ 'isTablet',
142
+ 'isDesktop',
143
+ 'id',
144
+ 'children',
145
+ 'device',
146
+ 'is',
147
+ 'unknown',
148
+ 'callback',
149
+ ],
150
+ customGroups: {
151
+ id: 'id',
152
+ children: 'children',
153
+ isMobile: 'isMobile*',
154
+ isTablet: 'isTablet*',
155
+ isDesktop: 'isDesktop*',
156
+ is: 'is*',
157
+ callback: 'on*',
158
+ },
159
+ },
160
+ ],
161
+
162
+ // Props
163
+ 'perfectionist/sort-jsx-props': [
164
+ 'error',
165
+ {
166
+ type: 'natural',
167
+ order: 'asc',
168
+ groups: [
169
+ 'top',
170
+ ...eslintStyleProps,
171
+ 'shorthand',
172
+ 'unknown',
173
+ 'multiline',
174
+ 'is',
175
+ 'set',
176
+ 'callback',
177
+ ],
178
+ customGroups: {
179
+ top: ['className', 'key', 'ref', 'name'],
180
+ is: 'is*',
181
+ callback: 'on*',
182
+ set: ['set*'],
183
+ ...eslintStylePropsCustomGroups,
184
+ },
185
+ },
186
+ ],
187
+
188
+ // Types
189
+ 'perfectionist/sort-object-types': [
190
+ 'error',
191
+ {
192
+ type: 'natural',
193
+ order: 'asc',
194
+ groups: ['top'],
195
+ customGroups: { top: 'id' },
196
+ },
197
+ ],
198
+
199
+ // Interfaces
200
+ 'perfectionist/sort-interfaces': [
201
+ 'error',
202
+ {
203
+ type: 'natural',
204
+ order: 'asc',
205
+ groups: ['top', 'unknown', 'is', 'callback', 'set'],
206
+ customGroups: {
207
+ top: ['id', '*Id'],
208
+ is: 'is*',
209
+ callback: 'on*',
210
+ set: ['set*'],
211
+ },
212
+ },
213
+ ],
214
+
215
+ // Maps, Enum, Union types, Intersection types
216
+ 'perfectionist/sort-maps': ['error', { type: 'natural', order: 'asc' }],
217
+ 'perfectionist/sort-enums': ['error', { type: 'natural', order: 'asc' }],
218
+ 'perfectionist/sort-union-types': ['error', { type: 'natural', order: 'asc' }],
219
+ 'perfectionist/sort-intersection-types': ['error', { type: 'natural', order: 'asc' }],
220
+ },
221
+ },
222
+ ]
223
+
224
+ export default eslintConfig
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@itcase/lint",
3
+ "version": "1.0.0",
4
+ "author": "ITCase",
5
+ "description": "Code style linter configuration presets",
6
+ "engines": {
7
+ "node": ">=20.12.0"
8
+ },
9
+ "scripts": {
10
+ "prepare": "husky",
11
+ "semantic-release": "semantic-release"
12
+ },
13
+ "license": "MIT",
14
+ "private": false,
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/ITCase/itcase-lint.git"
18
+ },
19
+ "files": [
20
+ "eslint",
21
+ "prettier",
22
+ "stylelint",
23
+ "README.md"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "registry": "https://registry.npmjs.org/"
28
+ },
29
+ "dependencies": {
30
+ "@babel/eslint-parser": "^7.24.8",
31
+ "@eslint/compat": "^1.1.1",
32
+ "@eslint/js": "^9.7.0",
33
+ "@ianvs/prettier-plugin-sort-imports": "^4.3.1",
34
+ "eslint": "^9.7.0",
35
+ "eslint-config-prettier": "^9.1.0",
36
+ "eslint-plugin-mobx": "^0.0.11",
37
+ "eslint-plugin-n": "17.10.0",
38
+ "eslint-plugin-node": "11.1.0",
39
+ "eslint-plugin-perfectionist": "^3.0.0",
40
+ "eslint-plugin-prettier": "5.2.1",
41
+ "eslint-plugin-promise": "7.0.0",
42
+ "eslint-plugin-react": "^7.35.0",
43
+ "eslint-plugin-react-hooks": "4.6.2",
44
+ "eslint-plugin-react-refresh": "^0.4.9",
45
+ "typescript-eslint": "^7.17.0",
46
+ "stylelint-config-standard": "^36.0.1",
47
+ "stylelint-no-unsupported-browser-features": "^8.0.1",
48
+ "stylelint-order": "^6.0.4",
49
+ "stylelint-prettier": "^5.0.2"
50
+ },
51
+ "peerDependencies": {
52
+ "eslint": "^9.7.0",
53
+ "prettier": "^3.3.3",
54
+ "stylelint": "^16.7.0"
55
+ },
56
+ "devDependencies": {
57
+ "@commitlint/cli": "^19.3.0",
58
+ "@commitlint/config-conventional": "^19.2.2",
59
+ "@semantic-release/changelog": "^6.0.3",
60
+ "@semantic-release/git": "^10.0.1",
61
+ "@semantic-release/release-notes-generator": "14.0.1",
62
+ "conventional-changelog-conventionalcommits": "^8.0.0",
63
+ "husky": "^9.1.2",
64
+ "lint-staged": "^15.2.7",
65
+ "react": "^18.3.1",
66
+ "semantic-release": "^24.0.0",
67
+ "typescript": "^5.5.4"
68
+ }
69
+ }
@@ -0,0 +1,27 @@
1
+ export default {
2
+ arrowParens: 'always',
3
+ importOrder: [
4
+ '^react$',
5
+ '^react-native$',
6
+ '^prop-types$',
7
+ '',
8
+ '<THIRD_PARTY_MODULES>',
9
+ '',
10
+ '^@itcase/(.*)$',
11
+ '',
12
+ '^(?!.*[.]css)src/(?!icons)(?!stores)(.*)$',
13
+ '^src/stores/(.*)$',
14
+ '^(?!.*[.]css)[.]',
15
+ '',
16
+ '^src/icons$',
17
+ '^src/icons/(.*)$',
18
+ '(.*).css$',
19
+ '',
20
+ ],
21
+ importOrderSortSpecifiers: true,
22
+ plugins: ['@ianvs/prettier-plugin-sort-imports'],
23
+ printWidth: 100,
24
+ semi: false,
25
+ singleQuote: true,
26
+ tabWidth: 2,
27
+ }
package/readme.md ADDED
@@ -0,0 +1,86 @@
1
+ # ITCase Lint
2
+
3
+ Пакет с пресетами конфигураций линтеров.
4
+
5
+ ## Использование
6
+
7
+ ### Установка
8
+
9
+ ```bash
10
+ $ npm i -D @itcase/lint eslint stylelint prettier
11
+ ```
12
+
13
+ ### Конфигурация
14
+
15
+ #### С помощью `package.json`
16
+
17
+ ```json
18
+ {
19
+ "prettier": "@itcase/lint/prettier",
20
+ "eslintConfig": {
21
+ "extends": "./node_modules/@itcase/lint/eslint/index.js"
22
+ },
23
+ "stylelint": {
24
+ "extends": "@itcase/lint/stylelint"
25
+ }
26
+ }
27
+ ```
28
+
29
+ #### С помощью отдельных конфигурационных файлов
30
+
31
+ ##### ESLint
32
+
33
+ Создать в корне проекта файл `.eslintrc.js` со следующим содержимым:
34
+
35
+ ```js
36
+ module.exports = {
37
+ extends: require.resolve('@itcase/lint/eslint'),
38
+ };
39
+ ```
40
+
41
+ ##### Stylelint
42
+
43
+ Создать в корне проекта файл `stylelint.config.js` со следующим содержимым:
44
+
45
+ ```js
46
+ module.exports = {
47
+ extends: require.resolve('@itcase/lint/stylelint'),
48
+ };
49
+ ```
50
+
51
+ ##### Prettier
52
+
53
+ Создать в корне проекта файл `.prettierrc.js` со следующим содержимым:
54
+
55
+ ```js
56
+ module.exports = require('@itcase/lint/prettier');
57
+ ```
58
+
59
+ ### Настройка git-хуков
60
+
61
+ Удобно использовать `husky` в связке с `lint-staged`, для этого необходимо:
62
+
63
+ 1. Установить пакеты
64
+
65
+ ```bash
66
+ npm i -D husky lint-staged
67
+ ```
68
+
69
+ 2. Создать в корне проекта файл `.lintstagedrc` со следующим содержимым:
70
+
71
+ ```json
72
+ {
73
+ "*.css": [
74
+ "npx stylelint --fix"
75
+ ],
76
+ "*.(js|jsx|ts|tsx)": [
77
+ "npx eslint --fix"
78
+ ]
79
+ }
80
+
81
+ ```
82
+
83
+ 3. Добавить **pre-commit** хук согласно документации husky: `npx lint-staged`
84
+
85
+ https://typicode.github.io/husky/how-to.html#adding-a-new-hook
86
+
@@ -0,0 +1,206 @@
1
+ export default {
2
+ extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'],
3
+ ignoreFiles: [
4
+ '**/__*.css',
5
+ '**/import.css',
6
+ '**/vendor/**/*.css',
7
+ '**/vendor/*.css',
8
+ '__*.css',
9
+ './**/*.ts',
10
+ './**/*.tsx',
11
+ './**/*.js',
12
+ './**/*.mjs',
13
+ ],
14
+ plugins: ['stylelint-prettier', 'stylelint-order', 'stylelint-no-unsupported-browser-features'],
15
+ rules: {
16
+ 'prettier/prettier': true,
17
+ 'plugin/no-unsupported-browser-features': [
18
+ true,
19
+ {
20
+ ignore: ['css-nesting', 'prefers-color-scheme'],
21
+ severity: 'warning',
22
+ },
23
+ ],
24
+ 'at-rule-empty-line-before': [
25
+ 'always',
26
+ {
27
+ except: ['inside-block'],
28
+ ignore: [
29
+ 'after-comment',
30
+ 'first-nested',
31
+ 'blockless-after-same-name-blockless',
32
+ 'blockless-after-blockless',
33
+ ],
34
+ },
35
+ ],
36
+ 'alpha-value-notation': [
37
+ 'number',
38
+ {
39
+ exceptProperties: ['opacity'],
40
+ },
41
+ ],
42
+ 'selector-nested-pattern': '&_*',
43
+ 'selector-class-pattern':
44
+ '^[a-z]([-]?[a-z0-9]+)*(__[a-z0-9]([-]?[a-z0-9]+)*)?(--[a-z0-9]([-]?[a-z0-9]+)*)?$',
45
+ 'custom-property-empty-line-before': null,
46
+ 'custom-property-pattern': '[a-z].*',
47
+ 'block-no-empty': null,
48
+ 'color-function-notation': 'legacy',
49
+ 'comment-empty-line-before': 'never',
50
+ 'declaration-empty-line-before': 'never',
51
+ 'function-url-quotes': 'always',
52
+ 'hue-degree-notation': 'number',
53
+ 'import-notation': 'string',
54
+ 'max-nesting-depth': 20,
55
+ 'no-descending-specificity': null,
56
+ 'property-disallowed-list': ['/^margin-/', '/^padding-/'],
57
+ 'property-no-unknown': [
58
+ true,
59
+ {
60
+ ignoreProperties: [
61
+ 'letter-spacing-range',
62
+ 'aspect-ratio',
63
+ 'font-range',
64
+ 'line-height-range',
65
+ ],
66
+ },
67
+ ],
68
+ 'selector-type-no-unknown': [
69
+ true,
70
+ {
71
+ ignoreTypes: ['/^/'],
72
+ },
73
+ ],
74
+ 'shorthand-property-no-redundant-values': null,
75
+ 'order/order': [
76
+ [
77
+ 'declarations',
78
+ {
79
+ type: 'at-rule',
80
+ name: 'mixin',
81
+ },
82
+ {
83
+ type: 'at-rule',
84
+ name: 'media',
85
+ },
86
+ {
87
+ type: 'rule',
88
+ selector: '^&::(before|after)',
89
+ },
90
+ {
91
+ type: 'rule',
92
+ selector: '^&:\\w',
93
+ },
94
+ {
95
+ type: 'rule',
96
+ selector: '^&_',
97
+ },
98
+ {
99
+ type: 'rule',
100
+ selector: '^.',
101
+ },
102
+ ],
103
+ {
104
+ unspecified: 'bottom',
105
+ },
106
+ ],
107
+ 'order/properties-order': [
108
+ [
109
+ 'width',
110
+ 'height',
111
+ 'min-width',
112
+ 'min-height',
113
+ 'max-width',
114
+ 'max-height',
115
+ 'aspect-ratio',
116
+ 'font',
117
+ 'font-size',
118
+ 'font-range',
119
+ 'font-weight',
120
+ 'color',
121
+ 'text-shadow',
122
+ 'text-decoration',
123
+ 'text-transform',
124
+ 'text-align',
125
+ 'text-rendering',
126
+ 'white-space',
127
+ 'line-height',
128
+ 'line-height-range',
129
+ 'letter-spacing',
130
+ 'background',
131
+ 'background-position',
132
+ 'background-repeat',
133
+ 'background-size',
134
+ 'padding',
135
+ 'margin',
136
+ 'border',
137
+ 'border-width',
138
+ 'border-color',
139
+ 'border-style',
140
+ 'border-radius',
141
+ 'border-left',
142
+ 'border-top',
143
+ 'border-right',
144
+ 'border-bottom',
145
+ 'float',
146
+ 'clear',
147
+ 'position',
148
+ 'overflow',
149
+ 'content',
150
+ 'display',
151
+ 'flex',
152
+ 'flex-direction',
153
+ 'flex-wrap',
154
+ 'flex-flow',
155
+ 'order',
156
+ 'justify-content',
157
+ 'align-items',
158
+ 'align-self',
159
+ 'align-content',
160
+ 'flex-grow',
161
+ 'flex-shrink',
162
+ 'flex-basis',
163
+ 'visibility',
164
+ 'box-sizing',
165
+ 'box-shadow',
166
+ 'left',
167
+ 'top',
168
+ 'right',
169
+ 'bottom',
170
+ 'vertical-align',
171
+ 'z-index',
172
+ 'transform',
173
+ 'transition',
174
+ 'animation',
175
+ 'animation-delay',
176
+ 'animation-direction',
177
+ 'animation-duration',
178
+ 'animation-fill-mode',
179
+ 'animation-iteration-count',
180
+ 'animation-name',
181
+ 'animation-play-state',
182
+ 'animation-timing-function',
183
+ 'opacity',
184
+ 'cursor',
185
+ ],
186
+ ],
187
+ 'rule-empty-line-before': [
188
+ 'always',
189
+ {
190
+ except: ['first-nested', 'inside-block-and-after-rule'],
191
+ ignore: ['inside-block'],
192
+ },
193
+ ],
194
+ 'keyframes-name-pattern': [
195
+ '^([a-z]+)([A-Z]?[a-z]+)+$',
196
+ { message: 'Expected keyframe name to be camelCase' },
197
+ ],
198
+ 'function-no-unknown': [true, { ignoreFunctions: ['$'] }],
199
+ 'at-rule-no-unknown': [
200
+ true,
201
+ {
202
+ ignoreAtRules: ['import-glob', 'for', 'each', 'mixin', 'define-mixin'],
203
+ },
204
+ ],
205
+ },
206
+ }