eslint-config-agent 1.9.3 → 2.0.1
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/overrides.js +0 -9
- package/index.js +12 -16
- package/package.json +21 -25
- package/plugins/index.js +2 -11
- package/rules/error-only-exports/index.js +22 -20
- package/rules/index.js +0 -3
- package/rules/jsx-classname-required/index.js +0 -95
package/configs/overrides.js
CHANGED
|
@@ -86,15 +86,6 @@ export const overridesConfig = (
|
|
|
86
86
|
},
|
|
87
87
|
},
|
|
88
88
|
|
|
89
|
-
// className requirement for JSX files
|
|
90
|
-
{
|
|
91
|
-
files: ['**/*.{tsx,jsx}'],
|
|
92
|
-
ignores: ['**/*.stories.{js,jsx,ts,tsx}'],
|
|
93
|
-
rules: {
|
|
94
|
-
'custom/jsx-classname-required': 'error',
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
|
|
98
89
|
// Function and file length rules - strict error thresholds
|
|
99
90
|
{
|
|
100
91
|
files: ['**/*.{ts,tsx,js,jsx}'],
|
package/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import js from '@eslint/js'
|
|
2
|
+
import earlyReturn from 'eslint-plugin-early-return'
|
|
3
|
+
import jsxClassname from 'eslint-plugin-jsx-classname'
|
|
2
4
|
import reactHooks from 'eslint-plugin-react-hooks'
|
|
3
5
|
import { defineConfig } from 'eslint/config'
|
|
4
6
|
import tseslint from 'typescript-eslint'
|
|
@@ -104,23 +106,10 @@ const config = defineConfig([
|
|
|
104
106
|
{
|
|
105
107
|
plugins,
|
|
106
108
|
},
|
|
107
|
-
|
|
108
|
-
...(reactHooks.configs['recommended-latest']
|
|
109
|
-
? [reactHooks.configs['recommended-latest']]
|
|
110
|
-
: [
|
|
111
|
-
{
|
|
112
|
-
name: 'react-hooks/recommended-flat',
|
|
113
|
-
plugins: {
|
|
114
|
-
'react-hooks': reactHooks,
|
|
115
|
-
},
|
|
116
|
-
rules: {
|
|
117
|
-
'react-hooks/rules-of-hooks': 'error',
|
|
118
|
-
'react-hooks/exhaustive-deps': 'warn',
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
]),
|
|
109
|
+
reactHooks.configs['recommended-latest'],
|
|
122
110
|
js.configs.recommended,
|
|
123
111
|
...tseslint.configs.recommended,
|
|
112
|
+
earlyReturn.configs.recommended,
|
|
124
113
|
|
|
125
114
|
// Base plugin strict configs (error, default, guard-clauses)
|
|
126
115
|
...basePluginsConfig,
|
|
@@ -153,7 +142,14 @@ const config = defineConfig([
|
|
|
153
142
|
// Examples files configuration
|
|
154
143
|
...examplesConfig,
|
|
155
144
|
|
|
156
|
-
//
|
|
145
|
+
// className requirement for JSX files (strict: errors + rejects Tailwind-only classNames)
|
|
146
|
+
{
|
|
147
|
+
...jsxClassname.configs.strict,
|
|
148
|
+
files: ['**/*.{tsx,jsx}'],
|
|
149
|
+
ignores: ['**/*.stories.{js,jsx,ts,tsx}'],
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// Final overrides (index files, switch case, length rules)
|
|
157
153
|
...overridesConfig(sharedRestrictedSyntax, tsOnlyRestrictedSyntax),
|
|
158
154
|
])
|
|
159
155
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-config-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "ESLint configuration package with TypeScript support",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -61,37 +61,17 @@
|
|
|
61
61
|
"author": "tupe12334",
|
|
62
62
|
"repository": {
|
|
63
63
|
"type": "git",
|
|
64
|
-
"url": "git+https://github.com/tupe12334/eslint-config.git"
|
|
64
|
+
"url": "git+https://github.com/tupe12334/eslint-config-agent.git"
|
|
65
65
|
},
|
|
66
66
|
"bugs": {
|
|
67
|
-
"url": "https://github.com/tupe12334/eslint-config/issues"
|
|
67
|
+
"url": "https://github.com/tupe12334/eslint-config-agent/issues"
|
|
68
68
|
},
|
|
69
|
-
"homepage": "https://github.com/tupe12334/eslint-config#readme",
|
|
69
|
+
"homepage": "https://github.com/tupe12334/eslint-config-agent#readme",
|
|
70
70
|
"license": "MIT",
|
|
71
71
|
"publishConfig": {
|
|
72
72
|
"access": "public"
|
|
73
73
|
},
|
|
74
74
|
"packageManager": "pnpm@10.18.2",
|
|
75
|
-
"peerDependencies": {
|
|
76
|
-
"@eslint/eslintrc": "^3.3.1",
|
|
77
|
-
"@eslint/js": "^9.34.0",
|
|
78
|
-
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
|
79
|
-
"@typescript-eslint/parser": "^8.40.0",
|
|
80
|
-
"eslint": "^9.34.0",
|
|
81
|
-
"eslint-plugin-class-export": "^1.0.1",
|
|
82
|
-
"eslint-plugin-import": "^2.32.0",
|
|
83
|
-
"eslint-plugin-n": "^17.21.3",
|
|
84
|
-
"eslint-plugin-react": "^7.37.5",
|
|
85
|
-
"eslint-plugin-react-hooks": "^5.2.0",
|
|
86
|
-
"eslint-plugin-security": "^3.0.1",
|
|
87
|
-
"eslint-plugin-storybook": "^9.1.5",
|
|
88
|
-
"typescript-eslint": "^8.40.0"
|
|
89
|
-
},
|
|
90
|
-
"peerDependenciesMeta": {
|
|
91
|
-
"eslint-plugin-preact": {
|
|
92
|
-
"optional": true
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
75
|
"devDependencies": {
|
|
96
76
|
"@commitlint/cli": "^20.1.0",
|
|
97
77
|
"@commitlint/config-conventional": "^20.0.0",
|
|
@@ -119,12 +99,28 @@
|
|
|
119
99
|
"typescript-eslint": "^8.46.1"
|
|
120
100
|
},
|
|
121
101
|
"dependencies": {
|
|
102
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
103
|
+
"@eslint/js": "^9.34.0",
|
|
104
|
+
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
|
105
|
+
"@typescript-eslint/parser": "^8.40.0",
|
|
106
|
+
"eslint": "^9.34.0",
|
|
107
|
+
"eslint-plugin-class-export": "^1.0.1",
|
|
122
108
|
"eslint-plugin-ddd": "^0.5.2",
|
|
123
109
|
"eslint-plugin-default": "^1.1.0",
|
|
110
|
+
"eslint-plugin-early-return": "^1.0.0",
|
|
124
111
|
"eslint-plugin-error": "^1.3.0",
|
|
112
|
+
"eslint-plugin-import": "^2.32.0",
|
|
113
|
+
"eslint-plugin-jsx-classname": "^1.0.1",
|
|
114
|
+
"eslint-plugin-n": "^17.21.3",
|
|
125
115
|
"eslint-plugin-no-optional-chaining": "^1.0.0",
|
|
116
|
+
"eslint-plugin-preact": "^0.1.0",
|
|
117
|
+
"eslint-plugin-react": "^7.37.5",
|
|
118
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
126
119
|
"eslint-plugin-required-exports": "^0.2.0",
|
|
120
|
+
"eslint-plugin-security": "^3.0.1",
|
|
127
121
|
"eslint-plugin-single-export": "^1.1.2",
|
|
128
|
-
"
|
|
122
|
+
"eslint-plugin-storybook": "^9.1.5",
|
|
123
|
+
"globals": "^16.4.0",
|
|
124
|
+
"typescript-eslint": "^8.40.0"
|
|
129
125
|
}
|
|
130
126
|
}
|
package/plugins/index.js
CHANGED
|
@@ -17,17 +17,9 @@ import errorPlugin from 'eslint-plugin-error'
|
|
|
17
17
|
import defaultPlugin from 'eslint-plugin-default'
|
|
18
18
|
import noOptionalChainingPlugin from 'eslint-plugin-no-optional-chaining'
|
|
19
19
|
import dddPlugin from 'eslint-plugin-ddd'
|
|
20
|
-
import
|
|
20
|
+
import preactPlugin from 'eslint-plugin-preact'
|
|
21
21
|
import { noDefaultClassExportRule } from '../rules/no-default-class-export/index.js'
|
|
22
22
|
|
|
23
|
-
// Conditionally import preact plugin if available
|
|
24
|
-
let preactPlugin = null
|
|
25
|
-
try {
|
|
26
|
-
preactPlugin = (await import('eslint-plugin-preact')).default
|
|
27
|
-
} catch {
|
|
28
|
-
// eslint-plugin-preact is not available
|
|
29
|
-
}
|
|
30
|
-
|
|
31
23
|
// Centralized plugin configuration
|
|
32
24
|
export const plugins = {
|
|
33
25
|
react: reactPlugin,
|
|
@@ -42,11 +34,10 @@ export const plugins = {
|
|
|
42
34
|
default: defaultPlugin,
|
|
43
35
|
'no-optional-chaining': noOptionalChainingPlugin,
|
|
44
36
|
ddd: dddPlugin,
|
|
45
|
-
|
|
37
|
+
preact: preactPlugin,
|
|
46
38
|
custom: {
|
|
47
39
|
rules: {
|
|
48
40
|
'no-default-class-export': noDefaultClassExportRule,
|
|
49
|
-
'jsx-classname-required': allRules.jsxClassNameRequiredRule,
|
|
50
41
|
},
|
|
51
42
|
},
|
|
52
43
|
}
|
|
@@ -119,26 +119,28 @@ export const errorOnlyExportsRule = {
|
|
|
119
119
|
// Only check on program exit to ensure we've seen all exports
|
|
120
120
|
},
|
|
121
121
|
'Program:exit'(node) {
|
|
122
|
-
if (isErrorOnlyFile(context)) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
122
|
+
if (!isErrorOnlyFile(context)) {
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check if the file already has the eslint-disable comment
|
|
127
|
+
const sourceCode = context.sourceCode || context.getSourceCode()
|
|
128
|
+
const comments = sourceCode.getAllComments()
|
|
129
|
+
|
|
130
|
+
const hasDisableComment = comments.some(
|
|
131
|
+
comment =>
|
|
132
|
+
comment.value.includes('eslint-disable ddd/require-spec-file') ||
|
|
133
|
+
comment.value.includes(
|
|
134
|
+
'eslint-disable-next-line ddd/require-spec-file'
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
// Only report if the disable comment is not present
|
|
139
|
+
if (!hasDisableComment) {
|
|
140
|
+
context.report({
|
|
141
|
+
node,
|
|
142
|
+
messageId: 'errorOnlyFile',
|
|
143
|
+
})
|
|
142
144
|
}
|
|
143
145
|
},
|
|
144
146
|
}
|
package/rules/index.js
CHANGED
|
@@ -22,7 +22,6 @@ import { noTypeAssertionsConfig } from './no-type-assertions/index.js'
|
|
|
22
22
|
import { noExportSpecifiersConfig } from './no-empty-exports/index.js'
|
|
23
23
|
import { noClassPropertyDefaultsConfig } from './no-class-property-defaults/index.js'
|
|
24
24
|
import { noDefaultClassExportRules } from './no-default-class-export/index.js'
|
|
25
|
-
import { jsxClassNameRequiredRule } from './jsx-classname-required/index.js'
|
|
26
25
|
import { noNullishCoalescingConfig } from './nullish-coalescing/index.js'
|
|
27
26
|
import { switchStatementsReturnTypeConfigs } from './switch-statements-return-type/index.js'
|
|
28
27
|
import { switchCaseFunctionsReturnTypeConfigs } from './switch-case-functions-return-type/index.js'
|
|
@@ -49,7 +48,6 @@ const allRules = {
|
|
|
49
48
|
noExportSpecifiersConfig,
|
|
50
49
|
noClassPropertyDefaultsConfig,
|
|
51
50
|
noDefaultClassExportRules,
|
|
52
|
-
jsxClassNameRequiredRule,
|
|
53
51
|
noNullishCoalescingConfig,
|
|
54
52
|
switchStatementsReturnTypeConfigs,
|
|
55
53
|
switchCaseFunctionsReturnTypeConfigs,
|
|
@@ -79,7 +77,6 @@ export {
|
|
|
79
77
|
noExportSpecifiersConfig,
|
|
80
78
|
noClassPropertyDefaultsConfig,
|
|
81
79
|
noDefaultClassExportRules,
|
|
82
|
-
jsxClassNameRequiredRule,
|
|
83
80
|
noNullishCoalescingConfig,
|
|
84
81
|
switchStatementsReturnTypeConfigs,
|
|
85
82
|
switchCaseFunctionsReturnTypeConfigs,
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESLint rule to require className attribute on HTML elements in JSX
|
|
3
|
-
*
|
|
4
|
-
* This rule enforces that all HTML elements in JSX must have a non-empty className attribute.
|
|
5
|
-
* It excludes React components (starting with capital letters), fragments, and title elements.
|
|
6
|
-
*
|
|
7
|
-
* Examples:
|
|
8
|
-
* - ❌ <div>Content</div>
|
|
9
|
-
* - ❌ <div className="">Content</div>
|
|
10
|
-
* - ✅ <div className="container">Content</div>
|
|
11
|
-
* - ✅ <MyComponent>Content</MyComponent> (ignored - React component)
|
|
12
|
-
* - ✅ <Fragment>Content</Fragment> (ignored - fragment)
|
|
13
|
-
* - ✅ <React.Fragment>Content</React.Fragment> (ignored - React fragment)
|
|
14
|
-
* - ✅ <React.StrictMode>Content</React.StrictMode> (ignored - React component)
|
|
15
|
-
* - ✅ <title>Page Title</title> (ignored - title element)
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const jsxClassNameRequiredRule = {
|
|
19
|
-
meta: {
|
|
20
|
-
type: 'layout',
|
|
21
|
-
docs: {
|
|
22
|
-
description:
|
|
23
|
-
'Require non-empty className attribute on HTML elements in JSX',
|
|
24
|
-
category: 'Stylistic Issues',
|
|
25
|
-
recommended: false,
|
|
26
|
-
},
|
|
27
|
-
fixable: null,
|
|
28
|
-
schema: [],
|
|
29
|
-
messages: {
|
|
30
|
-
missingClassName: 'HTML elements must have a className attribute.',
|
|
31
|
-
emptyClassName:
|
|
32
|
-
'HTML elements must have a non-empty className attribute.',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
create(context) {
|
|
37
|
-
return {
|
|
38
|
-
JSXOpeningElement(node) {
|
|
39
|
-
// Skip if this is a React component (starts with capital letter)
|
|
40
|
-
if (
|
|
41
|
-
node.name.type === 'JSXIdentifier' &&
|
|
42
|
-
/^[A-Z]/.test(node.name.name)
|
|
43
|
-
) {
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Skip if this is Fragment
|
|
48
|
-
if (
|
|
49
|
-
node.name.type === 'JSXIdentifier' &&
|
|
50
|
-
node.name.name === 'Fragment'
|
|
51
|
-
) {
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Skip if this is React.Something (like React.Fragment, React.StrictMode, etc.)
|
|
56
|
-
if (
|
|
57
|
-
node.name.type === 'JSXMemberExpression' &&
|
|
58
|
-
node.name.object.name === 'React' &&
|
|
59
|
-
/^[A-Z]/.test(node.name.property.name)
|
|
60
|
-
) {
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Skip if this is a title element (used in document head)
|
|
65
|
-
if (node.name.type === 'JSXIdentifier' && node.name.name === 'title') {
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Find className attribute
|
|
70
|
-
const classNameAttr = node.attributes.find(
|
|
71
|
-
attr => attr.type === 'JSXAttribute' && attr.name.name === 'className'
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
if (!classNameAttr) {
|
|
75
|
-
context.report({
|
|
76
|
-
node,
|
|
77
|
-
messageId: 'missingClassName',
|
|
78
|
-
})
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Check if className is an empty string
|
|
83
|
-
const value = classNameAttr.value
|
|
84
|
-
if (value && value.type === 'Literal' && value.value === '') {
|
|
85
|
-
context.report({
|
|
86
|
-
node,
|
|
87
|
-
messageId: 'emptyClassName',
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export { jsxClassNameRequiredRule }
|