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.
@@ -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:
@@ -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}'],
@@ -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
- // Final overrides (index files, switch case, className, length rules)
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.0.0",
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