@shayanthenerd/eslint-config 0.33.0 → 0.35.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/README.md CHANGED
@@ -35,7 +35,7 @@ Legend:
35
35
  | [TypeScript][plugin-ts] | 🔍  |
36
36
  | [Markdown][plugin-md] | ✅  |
37
37
  | [HTML][plugin-html] | ⚙️  |
38
- | [CSS][plugin-css] | ⚙️  |
38
+ | [CSS][plugin-css] ([eslint-cssicorn][plugin-cssicorn]) | ⚙️  |
39
39
  | **Formatting** | |
40
40
  | [Stylistic][plugin-stylistic] | ✅  |
41
41
  | [Perfectionist][plugin-perfectionist] | ✅  |
@@ -768,7 +768,7 @@ This project was inspired by the work of [Anthony Fu][antfu], whose generous con
768
768
  [MIT][license] License © 2025-PRESENT — [Shayan Zamani][ShayanTheNerd]
769
769
 
770
770
  <!-- Badges -->
771
- [license]: ./LICENSE
771
+ [license]: LICENSE
772
772
  [license-badge]: https://img.shields.io/badge/License-MIT-blue.svg?logoColor=FEFEFE&labelColor=3B82F6&color=3B82F6
773
773
  [npm-version-badge]: https://img.shields.io/npm/v/@shayanthenerd/eslint-config?label=&logo=npm&logoColor=FEFEFE&labelColor=3B82F6&color=3B82F6
774
774
  [npmx]: https://www.npmjs.com/package/@shayanthenerd/eslint-config
@@ -782,6 +782,7 @@ This project was inspired by the work of [Anthony Fu][antfu], whose generous con
782
782
  [plugin-astro]: https://ota-meshi.github.io/eslint-plugin-astro
783
783
  [plugin-baseline]: https://baselinejs.vercel.app
784
784
  [plugin-css]: https://github.com/eslint/css
785
+ [plugin-cssicorn]: https://github.com/sindresorhus/eslint-cssicorn
785
786
  [plugin-cypress]: https://github.com/cypress-io/eslint-plugin-cypress
786
787
  [plugin-html]: https://html-eslint.org
787
788
  [plugin-html-react]: https://html-eslint.org/docs/react/getting-started
@@ -815,7 +816,7 @@ This project was inspired by the work of [Anthony Fu][antfu], whose generous con
815
816
 
816
817
  <!-- References -->
817
818
  [antfu]: https://github.com/antfu
818
- [contributing]: ./.github/CONTRIBUTING.md
819
+ [contributing]: .github/CONTRIBUTING.md
819
820
  [eslint]: https://eslint.org
820
821
  [online-preview]: https://eslintconfig.netlify.app
821
822
  [eslint-config-ts-setup]: https://eslint.org/docs/latest/use/configure/configuration-files#typescript-configuration-files
@@ -3,12 +3,12 @@ import { defaultOptions } from "../helpers/options/defaultOptions.mjs";
3
3
  import { isEnabled } from "../utils/isEnabled.mjs";
4
4
  import { getCssRules } from "../rules/css.mjs";
5
5
  import { mergeConfigs } from "eslint-flat-config-utils";
6
- import eslintPluginUnicorn from "eslint-plugin-unicorn";
6
+ import eslintPluginCssicorn from "eslint-cssicorn";
7
7
  import eslintPluginCss from "@eslint/css";
8
8
  import { tailwind3, tailwind4 } from "tailwind-csstree";
9
9
  //#region src/configs/css.ts
10
10
  function getCssConfig(options) {
11
- const { css, unicorn, tailwind } = options.configs;
11
+ const { css, tailwind } = options.configs;
12
12
  const { overrides } = isEnabled(css) ? css : defaultOptions.configs.css;
13
13
  const tailwindSyntax = isEnabled(tailwind) && tailwind.entryPoint ? tailwind4 : tailwind3;
14
14
  const cssConfig = {
@@ -16,7 +16,7 @@ function getCssConfig(options) {
16
16
  files: [globs.css],
17
17
  plugins: {
18
18
  css: eslintPluginCss,
19
- ...isEnabled(unicorn) && { unicorn: eslintPluginUnicorn }
19
+ cssicorn: eslintPluginCssicorn
20
20
  },
21
21
  language: "css/css",
22
22
  languageOptions: {
@@ -23,11 +23,12 @@ function enableDetectedConfigs(options) {
23
23
  "storybook": options.configs?.test?.storybook === false,
24
24
  "@playwright/test": options.configs?.test?.playwright === false
25
25
  };
26
+ const isAstroDetected = isPackageDetected("astro", options);
26
27
  const autoDetectedPackages = {
27
28
  typescript: isPackageDetected("typescript", options),
28
29
  zod: isPackageDetected("zod", options),
29
- astro: isPackageDetected("astro", options),
30
- eslintPluginJsxA11yX: isPackageDetected("eslint-plugin-jsx-a11y-x", options),
30
+ astro: isAstroDetected,
31
+ eslintPluginJsxA11yX: isAstroDetected && isPackageDetected("eslint-plugin-jsx-a11y-x", options),
31
32
  react: isPackageDetected("react", options),
32
33
  next: isPackageDetected("next", options),
33
34
  tanstackStart: isPackageDetected("@tanstack/react-start", options),
@@ -1,6 +1,5 @@
1
1
  import { defaultOptions } from "../helpers/options/defaultOptions.mjs";
2
2
  import { isEnabled } from "../utils/isEnabled.mjs";
3
- import { isTruthy } from "../utils/isTruthy.mjs";
4
3
  //#region src/rules/css.ts
5
4
  const allowedPhysicalUnits = [
6
5
  "cqh",
@@ -47,10 +46,10 @@ const allowedPhysicalProperties = [
47
46
  "contain-intrinsic-height"
48
47
  ];
49
48
  function getCssRules(options) {
50
- const { css, vue, unicorn, tailwind, baseline } = options.configs;
49
+ const { css, tailwind, baseline } = options.configs;
51
50
  const { allowedUnknownPseudoSelectors: userAllowedUnknownPseudoSelectors } = isEnabled(css) ? css : defaultOptions.configs.css;
52
51
  const { allowedAtRules: userAllowedAtRules, allowedFunctions: userAllowedFunctions, allowedMediaConditions: userAllowedMediaConditions, allowedProperties: userAllowedProperties, allowedPropertyValues: userAllowedPropertyValues, allowedSelectors: userAllowedSelectors, allowedUnits: userAllowedUnits } = isEnabled(baseline) ? baseline.css : defaultOptions.configs.baseline.css;
53
- const cssRules = {
52
+ return {
54
53
  "css/font-family-fallbacks": "warn",
55
54
  "css/no-duplicate-imports": "error",
56
55
  "css/no-duplicate-keyframe-selectors": "error",
@@ -90,36 +89,56 @@ function getCssRules(options) {
90
89
  },
91
90
  allowSelectors: ["selection", ...userAllowedSelectors],
92
91
  allowUnits: userAllowedUnits
93
- }] : "off"
92
+ }] : "off",
93
+ "cssicorn/consistent-compound-selector-order": "warn",
94
+ "cssicorn/consistent-layer-order": ["warn", { checkUndeclaredLayers: true }],
95
+ "cssicorn/consistent-property-order": "warn",
96
+ "cssicorn/consistent-value-order": "warn",
97
+ "cssicorn/lowercase": "error",
98
+ "cssicorn/no-clamped-values": "error",
99
+ "cssicorn/no-declarations-after-nested-rules": "warn",
100
+ "cssicorn/no-deprecated-features": "error",
101
+ "cssicorn/no-descending-specificity": "warn",
102
+ "cssicorn/no-disabled-pinch-zoom": "error",
103
+ "cssicorn/no-duplicate-at-rule-definitions": "warn",
104
+ "cssicorn/no-duplicate-font-family-names": "error",
105
+ "cssicorn/no-duplicate-properties": "warn",
106
+ "cssicorn/no-duplicate-selectors": "warn",
107
+ "cssicorn/no-ineffective-keyframe-declarations": "warn",
108
+ "cssicorn/no-ineffective-overrides": "error",
109
+ "cssicorn/no-ineffective-properties": "error",
110
+ "cssicorn/no-ineffective-selector-properties": "error",
111
+ "cssicorn/no-ineffective-supports-conditions": "error",
112
+ "cssicorn/no-ineffective-transitions": "error",
113
+ "cssicorn/no-invalid-media-features": "error",
114
+ "cssicorn/no-invalid-property-references": "error",
115
+ "cssicorn/no-layout-animations": "warn",
116
+ "cssicorn/no-nesting-with-mixed-specificity": "warn",
117
+ "cssicorn/no-overflow-axis-coercion": "error",
118
+ "cssicorn/no-redundant-functions": "warn",
119
+ "cssicorn/no-redundant-nested-style-rules": "warn",
120
+ "cssicorn/no-redundant-nesting-selector": "warn",
121
+ "cssicorn/no-redundant-shorthand-values": "warn",
122
+ "cssicorn/no-self-referencing-custom-properties": "error",
123
+ "cssicorn/no-unknown-animations": "error",
124
+ "cssicorn/no-unknown-annotations": "error",
125
+ "cssicorn/no-unknown-pseudo-selectors": ["error", { allow: userAllowedUnknownPseudoSelectors }],
126
+ "cssicorn/no-unscoped-nesting-selector": isEnabled(tailwind) ? "error" : ["error", { scopingRootAtRules: [] }],
127
+ "cssicorn/no-useless-is": "warn",
128
+ "cssicorn/no-zero-length-unit": "warn",
129
+ "cssicorn/prefer-aspect-ratio": "warn",
130
+ "cssicorn/prefer-clamp": "warn",
131
+ "cssicorn/prefer-current-color": "warn",
132
+ "cssicorn/prefer-explicit-viewport-units": "warn",
133
+ "cssicorn/prefer-individual-transform-properties": "warn",
134
+ "cssicorn/prefer-light-dark": "warn",
135
+ "cssicorn/prefer-media-feature-range-syntax": "warn",
136
+ "cssicorn/prefer-merged-rules": "warn",
137
+ "cssicorn/prefer-modern-syntax": "warn",
138
+ "cssicorn/prefer-nesting": "warn",
139
+ "cssicorn/prefer-relative-colors": "warn",
140
+ "cssicorn/require-property-descriptors": "error"
94
141
  };
95
- const unicornRules = {
96
- "unicorn/no-deprecated-css-features": "error",
97
- "unicorn/no-duplicate-css-selectors": "warn",
98
- "unicorn/no-duplicate-font-family-names": "error",
99
- "unicorn/no-invalid-media-features": "error",
100
- "unicorn/no-nesting-with-mixed-specificity": "warn",
101
- "unicorn/no-redundant-nested-style-rules": "warn",
102
- "unicorn/no-shorthand-property-overrides": "warn",
103
- "unicorn/no-unknown-css-annotations": "error",
104
- "unicorn/no-unknown-pseudo-selectors": ["error", { allow: [
105
- "::-ms-reveal",
106
- ":-webkit-autofill",
107
- "::-webkit-inner-spin-button",
108
- "::-webkit-search-cancel-button",
109
- "::-webkit-search-results-button",
110
- ...isEnabled(vue) ? [
111
- ":deep",
112
- ":global",
113
- ":slotted"
114
- ] : [],
115
- ...userAllowedUnknownPseudoSelectors
116
- ].filter(isTruthy) }],
117
- "unicorn/no-unscoped-css-nesting-selector": isEnabled(tailwind) ? ["error", { scopingRootAtRules: ["utility", "custom-variant"] }] : "error",
118
- "unicorn/prefer-explicit-viewport-units": "warn",
119
- "unicorn/prefer-media-feature-range-syntax": "warn"
120
- };
121
- if (isEnabled(unicorn)) Object.assign(cssRules, unicornRules);
122
- return cssRules;
123
142
  }
124
143
  //#endregion
125
144
  export { getCssRules };
@@ -79,9 +79,16 @@ function getHtmlRules(options) {
79
79
  { enforceTemplatedAttrValue: true }
80
80
  ]
81
81
  };
82
+ const unicornRules = {
83
+ "unicorn/consistent-compound-words": "warn",
84
+ "unicorn/no-ineffective-csp-directives": "error",
85
+ "unicorn/no-invalid-file-input-accept": "error",
86
+ "unicorn/no-invalid-integrity": "error",
87
+ "unicorn/relative-url-style": "warn"
88
+ };
82
89
  if (isEnabled(stylistic)) Object.assign(htmlRules, stylisticRules);
90
+ if (isEnabled(unicorn)) Object.assign(htmlRules, unicornRules);
83
91
  if (isEnabled(tailwind)) htmlRules["better-tailwindcss/no-duplicate-classes"] = "off";
84
- if (isEnabled(unicorn)) htmlRules["unicorn/no-invalid-file-input-accept"] = "error";
85
92
  return htmlRules;
86
93
  }
87
94
  //#endregion
@@ -47,7 +47,13 @@ function getMarkdownRules(options) {
47
47
  "markdown/require-alt-text": "warn",
48
48
  "markdown/table-column-count": isCommonMark ? "off" : ["error", { checkMissingCells: true }]
49
49
  };
50
- if (isEnabled(unicorn)) markdownRules["unicorn/no-missing-local-resource"] = "error";
50
+ const unicornRules = {
51
+ "unicorn/no-javascript-url": "error",
52
+ "unicorn/no-empty-link-text": "error",
53
+ "unicorn/no-missing-local-resource": "error",
54
+ "unicorn/relative-url-style": "warn"
55
+ };
56
+ if (isEnabled(unicorn)) Object.assign(markdownRules, unicornRules);
51
57
  return markdownRules;
52
58
  }
53
59
  //#endregion
@@ -14,7 +14,7 @@ const commonCallees = [
14
14
  "ctl"
15
15
  ];
16
16
  function getReactRules(options) {
17
- const { react, unicorn, tailwind, baseline, stylistic, typescript } = options.configs;
17
+ const { react, tailwind, baseline, stylistic, typescript } = options.configs;
18
18
  const { imageComponents: userImageComponents, anchorComponents: userAnchorComponents, headingComponents: userHeadingComponents } = isEnabled(react) && isEnabled(react.accessibility) ? react.accessibility : defaultOptions.configs.react.accessibility;
19
19
  const isTypescriptEnabled = isEnabled(typescript);
20
20
  const reactRules = {
@@ -161,7 +161,6 @@ function getReactRules(options) {
161
161
  reactRules["better-tailwindcss/no-duplicate-classes"] = "off";
162
162
  if (tailwind.multilineSort) reactRules["@html-eslint/react/classname-spacing"] = "off";
163
163
  }
164
- if (isEnabled(unicorn)) reactRules["unicorn/no-invalid-file-input-accept"] = "error";
165
164
  return reactRules;
166
165
  }
167
166
  //#endregion
@@ -49,6 +49,7 @@ function getTypescriptRules(options) {
49
49
  "@typescript-eslint/no-unsafe-assignment": "warn",
50
50
  "@typescript-eslint/no-unsafe-call": "warn",
51
51
  "@typescript-eslint/no-unsafe-declaration-merging": "warn",
52
+ "@typescript-eslint/no-unsafe-enum-assignment": "error",
52
53
  "@typescript-eslint/no-unsafe-enum-comparison": "error",
53
54
  "@typescript-eslint/no-unsafe-function-type": "warn",
54
55
  "@typescript-eslint/no-unsafe-member-access": "warn",
@@ -64,11 +64,22 @@ function getUnicornRules(options) {
64
64
  "unicorn/no-for-loop": "warn",
65
65
  "unicorn/no-immediate-mutation": "warn",
66
66
  "unicorn/no-impossible-length-comparison": "error",
67
+ "unicorn/no-incomplete-accessor-override": "error",
67
68
  "unicorn/no-incorrect-query-selector": "error",
68
69
  "unicorn/no-incorrect-template-string-interpolation": "error",
70
+ "unicorn/no-ineffective-csp-directives": "error",
71
+ "unicorn/no-invalid-boolean-attribute-value": "error",
69
72
  "unicorn/no-invalid-character-comparison": "error",
73
+ "unicorn/no-invalid-dom-token": "error",
70
74
  "unicorn/no-invalid-fetch-options": "error",
75
+ "unicorn/no-invalid-file-input-accept": "error",
76
+ "unicorn/no-invalid-intl-options": "error",
77
+ "unicorn/no-invalid-property-descriptor": "error",
71
78
  "unicorn/no-invalid-remove-event-listener": "error",
79
+ "unicorn/no-invalid-response-options": "error",
80
+ "unicorn/no-invalid-style-set-property": "error",
81
+ "unicorn/no-invalid-temporal-arithmetic": "error",
82
+ "unicorn/no-invalid-url-protocol-comparison": "error",
72
83
  "unicorn/no-invalid-well-known-symbol-methods": "error",
73
84
  "unicorn/no-late-current-target-access": "warn",
74
85
  "unicorn/no-late-event-control": "error",
@@ -87,7 +98,9 @@ function getUnicornRules(options) {
87
98
  "unicorn/no-object-as-default-parameter": "warn",
88
99
  "unicorn/no-object-methods-with-collections": "warn",
89
100
  "unicorn/no-optional-chaining-on-undeclared-variable": "error",
101
+ "unicorn/no-prevent-default-in-passive-listener": "error",
90
102
  "unicorn/no-redundant-comparison": "warn",
103
+ "unicorn/no-repeated-call-wrappers": "error",
91
104
  "unicorn/no-return-array-push": "warn",
92
105
  "unicorn/no-selector-as-dom-name": "error",
93
106
  "unicorn/no-single-promise-in-promise-methods": "warn",
@@ -101,9 +114,11 @@ function getUnicornRules(options) {
101
114
  "unicorn/no-unnecessary-array-flat-depth": "warn",
102
115
  "unicorn/no-unnecessary-array-splice-count": "warn",
103
116
  "unicorn/no-unnecessary-boolean-comparison": "warn",
117
+ "unicorn/no-unnecessary-default-arguments": "warn",
104
118
  "unicorn/no-unnecessary-fetch-options": "warn",
105
119
  "unicorn/no-unnecessary-global-this": "warn",
106
120
  "unicorn/no-unnecessary-nested-ternary": "warn",
121
+ "unicorn/no-unnecessary-parameters": "warn",
107
122
  "unicorn/no-unnecessary-polyfills": "warn",
108
123
  "unicorn/no-unnecessary-slice-end": "warn",
109
124
  "unicorn/no-unnecessary-splice": "warn",
@@ -112,11 +127,13 @@ function getUnicornRules(options) {
112
127
  "unicorn/no-unreadable-iife": "warn",
113
128
  "unicorn/no-unreadable-new-expression": "warn",
114
129
  "unicorn/no-unsafe-buffer-conversion": "warn",
130
+ "unicorn/no-unsafe-json-serialization": "error",
115
131
  "unicorn/no-unsafe-promise-all-settled-values": "error",
116
132
  "unicorn/no-unsafe-property-key": "error",
117
133
  "unicorn/no-unused-builtin-method-return": "warn",
118
134
  "unicorn/no-unused-iterator-helper": "warn",
119
135
  "unicorn/no-unused-properties": "warn",
136
+ "unicorn/no-url-in-search-params": "warn",
120
137
  "unicorn/no-useless-boolean-cast": "warn",
121
138
  "unicorn/no-useless-collection-argument": "warn",
122
139
  "unicorn/no-useless-compound-assignment": "warn",
@@ -164,7 +181,10 @@ function getUnicornRules(options) {
164
181
  "unicorn/prefer-class-fields": "warn",
165
182
  "unicorn/prefer-classlist-toggle": "warn",
166
183
  "unicorn/prefer-code-point": "error",
167
- "unicorn/prefer-combined-guards": ["warn", { checkCompoundConditions: true }],
184
+ "unicorn/prefer-combined-guards": ["warn", {
185
+ checkCompoundConditions: true,
186
+ checkMultiStatementBodies: true
187
+ }],
168
188
  "unicorn/prefer-continue": ["warn", { maximumStatements: 3 }],
169
189
  "unicorn/prefer-date-now": "warn",
170
190
  "unicorn/prefer-direct-iteration": "warn",
@@ -195,6 +215,7 @@ function getUnicornRules(options) {
195
215
  "unicorn/prefer-iterator-zip": "warn",
196
216
  "unicorn/prefer-json-import": "warn",
197
217
  "unicorn/prefer-keyboard-event-key": "error",
218
+ "unicorn/prefer-literal-ascii": "warn",
198
219
  "unicorn/prefer-location-assign": "warn",
199
220
  "unicorn/prefer-logical-operator-over-ternary": "warn",
200
221
  "unicorn/prefer-map-from-entries": "warn",
@@ -219,6 +240,7 @@ function getUnicornRules(options) {
219
240
  "unicorn/prefer-optional-catch-binding": "warn",
220
241
  "unicorn/prefer-path2d": "warn",
221
242
  "unicorn/prefer-private-class-fields": "warn",
243
+ "unicorn/prefer-promise-static-methods": "warn",
222
244
  "unicorn/prefer-promise-try": "warn",
223
245
  "unicorn/prefer-promise-with-resolvers": "warn",
224
246
  "unicorn/prefer-queue-microtask": "warn",
@@ -229,9 +251,10 @@ function getUnicornRules(options) {
229
251
  "unicorn/prefer-scoped-selector": "warn",
230
252
  "unicorn/prefer-set-methods": "warn",
231
253
  "unicorn/prefer-set-size": "warn",
254
+ "unicorn/prefer-short-escape-sequences": "warn",
232
255
  "unicorn/prefer-simple-condition-first": "warn",
233
256
  "unicorn/prefer-simple-sort-comparator": "warn",
234
- "unicorn/prefer-simplified-conditions": "warn",
257
+ "unicorn/prefer-simplified-conditions": ["warn", { negatedConditions: "expand" }],
235
258
  "unicorn/prefer-single-array-predicate": "warn",
236
259
  "unicorn/prefer-single-call": "warn",
237
260
  "unicorn/prefer-single-object-destructuring": "warn",
@@ -267,6 +290,7 @@ function getUnicornRules(options) {
267
290
  "unicorn/require-number-to-fixed-digits-argument": "warn",
268
291
  "unicorn/require-passive-events": "warn",
269
292
  "unicorn/require-proxy-trap-boolean-return": "error",
293
+ "unicorn/require-text-decoder-streaming": "error",
270
294
  "unicorn/switch-case-braces": "warn",
271
295
  "unicorn/switch-case-break-position": "error",
272
296
  "unicorn/template-indent": "warn",