eslint-config-agent 2.0.0 → 2.1.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/examples.js +0 -1
- package/configs/overrides.js +0 -10
- package/configs/test-files.js +0 -1
- package/index.js +9 -2
- package/package.json +2 -1
- package/plugins/index.js +0 -2
- package/rules/index.js +0 -6
- package/rules/jsx-classname-required/index.js +0 -95
- package/rules/no-class-property-defaults/index.js +0 -37
package/configs/examples.js
CHANGED
|
@@ -55,7 +55,6 @@ const tsOnlyRestrictedSyntax = [
|
|
|
55
55
|
...noRecordLiteralTypesConfigs,
|
|
56
56
|
...noInlineUnionTypesConfigs,
|
|
57
57
|
allRules.noTypeAssertionsConfig,
|
|
58
|
-
allRules.noClassPropertyDefaultsConfig,
|
|
59
58
|
{
|
|
60
59
|
selector: 'TSAsExpression:has(> TSIndexedAccessType > TSTypeQuery)',
|
|
61
60
|
message:
|
package/configs/overrides.js
CHANGED
|
@@ -79,22 +79,12 @@ export const overridesConfig = (
|
|
|
79
79
|
'Type assertions with indexed access types like "as (typeof X)[number]" are not allowed. Use a named type instead.',
|
|
80
80
|
},
|
|
81
81
|
allRules.noTypeAssertionsConfig,
|
|
82
|
-
allRules.noClassPropertyDefaultsConfig,
|
|
83
82
|
allRules.noProcessEnvPropertiesConfig,
|
|
84
83
|
allRules.noExportSpecifiersConfig,
|
|
85
84
|
],
|
|
86
85
|
},
|
|
87
86
|
},
|
|
88
87
|
|
|
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
88
|
// Function and file length rules - strict error thresholds
|
|
99
89
|
{
|
|
100
90
|
files: ['**/*.{ts,tsx,js,jsx}'],
|
package/configs/test-files.js
CHANGED
|
@@ -70,7 +70,6 @@ const tsOnlyRestrictedSyntax = [
|
|
|
70
70
|
...noRecordLiteralTypesConfigs,
|
|
71
71
|
...noInlineUnionTypesConfigs,
|
|
72
72
|
allRules.noTypeAssertionsConfig,
|
|
73
|
-
allRules.noClassPropertyDefaultsConfig,
|
|
74
73
|
{
|
|
75
74
|
selector: 'TSAsExpression:has(> TSIndexedAccessType > TSTypeQuery)',
|
|
76
75
|
message:
|
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import js from '@eslint/js'
|
|
2
2
|
import earlyReturn from 'eslint-plugin-early-return'
|
|
3
|
+
import jsxClassname from 'eslint-plugin-jsx-classname'
|
|
3
4
|
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
5
|
import { defineConfig } from 'eslint/config'
|
|
5
6
|
import tseslint from 'typescript-eslint'
|
|
@@ -85,7 +86,6 @@ const tsOnlyRestrictedSyntax = [
|
|
|
85
86
|
...noRecordLiteralTypesConfigs,
|
|
86
87
|
...noInlineUnionTypesConfigs,
|
|
87
88
|
allRules.noTypeAssertionsConfig,
|
|
88
|
-
allRules.noClassPropertyDefaultsConfig,
|
|
89
89
|
{
|
|
90
90
|
selector: 'TSAsExpression:has(> TSIndexedAccessType > TSTypeQuery)',
|
|
91
91
|
message:
|
|
@@ -141,7 +141,14 @@ const config = defineConfig([
|
|
|
141
141
|
// Examples files configuration
|
|
142
142
|
...examplesConfig,
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// className requirement for JSX files (strict: errors + rejects Tailwind-only classNames)
|
|
145
|
+
{
|
|
146
|
+
...jsxClassname.configs.strict,
|
|
147
|
+
files: ['**/*.{tsx,jsx}'],
|
|
148
|
+
ignores: ['**/*.stories.{js,jsx,ts,tsx}'],
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// Final overrides (index files, switch case, length rules)
|
|
145
152
|
...overridesConfig(sharedRestrictedSyntax, tsOnlyRestrictedSyntax),
|
|
146
153
|
])
|
|
147
154
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-config-agent",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "ESLint configuration package with TypeScript support",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"eslint-plugin-early-return": "^1.0.0",
|
|
111
111
|
"eslint-plugin-error": "^1.3.0",
|
|
112
112
|
"eslint-plugin-import": "^2.32.0",
|
|
113
|
+
"eslint-plugin-jsx-classname": "^1.0.1",
|
|
113
114
|
"eslint-plugin-n": "^17.21.3",
|
|
114
115
|
"eslint-plugin-no-optional-chaining": "^1.0.0",
|
|
115
116
|
"eslint-plugin-preact": "^0.1.0",
|
package/plugins/index.js
CHANGED
|
@@ -18,7 +18,6 @@ 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
20
|
import preactPlugin from 'eslint-plugin-preact'
|
|
21
|
-
import allRules from '../rules/index.js'
|
|
22
21
|
import { noDefaultClassExportRule } from '../rules/no-default-class-export/index.js'
|
|
23
22
|
|
|
24
23
|
// Centralized plugin configuration
|
|
@@ -39,7 +38,6 @@ export const plugins = {
|
|
|
39
38
|
custom: {
|
|
40
39
|
rules: {
|
|
41
40
|
'no-default-class-export': noDefaultClassExportRule,
|
|
42
|
-
'jsx-classname-required': allRules.jsxClassNameRequiredRule,
|
|
43
41
|
},
|
|
44
42
|
},
|
|
45
43
|
}
|
package/rules/index.js
CHANGED
|
@@ -20,9 +20,7 @@ import {
|
|
|
20
20
|
import { noProcessEnvPropertiesConfig } from './no-process-env-properties/index.js'
|
|
21
21
|
import { noTypeAssertionsConfig } from './no-type-assertions/index.js'
|
|
22
22
|
import { noExportSpecifiersConfig } from './no-empty-exports/index.js'
|
|
23
|
-
import { noClassPropertyDefaultsConfig } from './no-class-property-defaults/index.js'
|
|
24
23
|
import { noDefaultClassExportRules } from './no-default-class-export/index.js'
|
|
25
|
-
import { jsxClassNameRequiredRule } from './jsx-classname-required/index.js'
|
|
26
24
|
import { noNullishCoalescingConfig } from './nullish-coalescing/index.js'
|
|
27
25
|
import { switchStatementsReturnTypeConfigs } from './switch-statements-return-type/index.js'
|
|
28
26
|
import { switchCaseFunctionsReturnTypeConfigs } from './switch-case-functions-return-type/index.js'
|
|
@@ -47,9 +45,7 @@ const allRules = {
|
|
|
47
45
|
noProcessEnvPropertiesConfig,
|
|
48
46
|
noTypeAssertionsConfig,
|
|
49
47
|
noExportSpecifiersConfig,
|
|
50
|
-
noClassPropertyDefaultsConfig,
|
|
51
48
|
noDefaultClassExportRules,
|
|
52
|
-
jsxClassNameRequiredRule,
|
|
53
49
|
noNullishCoalescingConfig,
|
|
54
50
|
switchStatementsReturnTypeConfigs,
|
|
55
51
|
switchCaseFunctionsReturnTypeConfigs,
|
|
@@ -77,9 +73,7 @@ export {
|
|
|
77
73
|
noProcessEnvPropertiesConfig,
|
|
78
74
|
noTypeAssertionsConfig,
|
|
79
75
|
noExportSpecifiersConfig,
|
|
80
|
-
noClassPropertyDefaultsConfig,
|
|
81
76
|
noDefaultClassExportRules,
|
|
82
|
-
jsxClassNameRequiredRule,
|
|
83
77
|
noNullishCoalescingConfig,
|
|
84
78
|
switchStatementsReturnTypeConfigs,
|
|
85
79
|
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 }
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule configuration for no-class-property-defaults
|
|
3
|
-
*
|
|
4
|
-
* This rule disallows class properties from having default values.
|
|
5
|
-
* Forces explicit initialization in constructor or through methods.
|
|
6
|
-
*
|
|
7
|
-
* Examples:
|
|
8
|
-
* - ❌ class User { name = 'default'; }
|
|
9
|
-
* - ❌ class User { count = 0; }
|
|
10
|
-
* - ❌ class User { items = []; }
|
|
11
|
-
* - ✅ class User { name; constructor() { this.name = 'default'; } }
|
|
12
|
-
* - ✅ class User { count; initialize() { this.count = 0; } }
|
|
13
|
-
*
|
|
14
|
-
* @see https://eslint.org/docs/latest/rules/no-restricted-syntax
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const rule = 'error'
|
|
18
|
-
|
|
19
|
-
const selector = 'PropertyDefinition[value]'
|
|
20
|
-
|
|
21
|
-
const message =
|
|
22
|
-
'Class properties cannot have default values. Initialize properties in the constructor or through methods instead.'
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Export the complete rule configuration for no-restricted-syntax
|
|
26
|
-
* Can be used in ESLint config as part of no-restricted-syntax rules:
|
|
27
|
-
* "no-restricted-syntax": ["error", ...otherRules, noClassPropertyDefaultsConfig]
|
|
28
|
-
*/
|
|
29
|
-
const noClassPropertyDefaultsConfig = {
|
|
30
|
-
selector,
|
|
31
|
-
message,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Consolidated exports
|
|
35
|
-
export { rule, selector, message, noClassPropertyDefaultsConfig }
|
|
36
|
-
|
|
37
|
-
export default noClassPropertyDefaultsConfig
|