@singlepixellab/eslint-config 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,17 @@
1
+ /** @type {import('eslint').Linter.Config[]} */
2
+ export default [
3
+ {
4
+ name: "spl/ignores",
5
+ ignores: [
6
+ "**/node_modules/**",
7
+ "**/.cache/**",
8
+ "**/.react-router/**",
9
+ "**/.next/**",
10
+ "**/.vercel/**",
11
+ "**/build/**",
12
+ "**/public/build/**",
13
+ "**/dist/**",
14
+ "**/coverage/**",
15
+ ],
16
+ },
17
+ ];
@@ -0,0 +1,130 @@
1
+ import importPlugin from "eslint-plugin-import";
2
+
3
+ /** @type {import('eslint').Linter.Config[]} */
4
+ export default [
5
+ importPlugin.flatConfigs.recommended,
6
+ {
7
+ name: "spl/imports",
8
+ files: ["**/*.{js,mjs,cjs}"],
9
+ rules: {
10
+ // Ensures imports point to a file/module that exists, prevents broken
11
+ // imports
12
+ "import/no-unresolved": [
13
+ "error",
14
+ { commonjs: true, caseSensitive: true },
15
+ ],
16
+
17
+ // Ensures named imports match the exported names, avoids silent bugs
18
+ "import/named": "error",
19
+
20
+ // We allow missing default exports; turning this off for flexibility
21
+ "import/default": "off",
22
+
23
+ // Allow full namespace imports (e.g. `import * as utils from './utils'`);
24
+ // turning this off for convenience
25
+ "import/namespace": "off",
26
+
27
+ // Ensures files that export something do so correctly,
28
+ // prevents mismatched export/import errors
29
+ "import/export": "error",
30
+
31
+ // Prevents confusing bugs when a named export shadows the default export,
32
+ // keeps default usage explicit
33
+ "import/no-named-as-default": "off",
34
+
35
+ // Avoids subtle issues when using a default export’s members as named
36
+ // imports
37
+ "import/no-named-as-default-member": "off",
38
+
39
+ // Mutable exports lead to unpredictable behavior, enforcing immutability
40
+ // improves reliability
41
+ "import/no-mutable-exports": "error",
42
+
43
+ // We don’t use AMD modules, this keeps code modern and avoids unsupported
44
+ // patterns
45
+ "import/no-amd": "error",
46
+
47
+ // Forces imports to come first, improves code structure and consistency
48
+ "import/first": "error",
49
+
50
+ // Prevents multiple imports from the same module, reduces clutter
51
+ "import/no-duplicates": "error",
52
+
53
+ // Enforces a specific order of imports, improves readability and
54
+ // navigation
55
+ "import/order": [
56
+ "error",
57
+ {
58
+ groups: [
59
+ ["builtin", "external", "internal", "parent", "sibling", "index"],
60
+ ],
61
+ },
62
+ ],
63
+
64
+ // Requires a newline after the import block, improves visual separation
65
+ "import/newline-after-import": "error",
66
+
67
+ // Disallows absolute paths in imports, enforces relative or module-based
68
+ // paths for better portability
69
+ "import/no-absolute-path": "error",
70
+
71
+ // Disallows `require()` with dynamic expressions, avoids unresolvable and
72
+ // unpredictable imports
73
+ "import/no-dynamic-require": "error",
74
+
75
+ // Prevents usage of Webpack-specific import syntax, makes code more
76
+ // tool-agnostic
77
+ "import/no-webpack-loader-syntax": "error",
78
+
79
+ // Prevents invalid usage of default exports as named, avoids runtime
80
+ // errors
81
+ "import/no-named-default": "error",
82
+
83
+ // We allow exports anywhere in a file, disabled to support flexible file
84
+ // structure
85
+ "import/exports-last": "off",
86
+
87
+ // We allow default exports, disabled to allow ergonomic export patterns
88
+ "import/no-default-export": "off",
89
+
90
+ // We allow named exports, disabled to support modular exports
91
+ "import/no-named-export": "off",
92
+
93
+ // Prevents files from importing themselves, avoids logical errors and
94
+ // infinite loops
95
+ "import/no-self-import": "error",
96
+
97
+ // Avoids circular dependencies, keeps module graph clean and maintainable
98
+ "import/no-cycle": ["error", { maxDepth: "∞" }],
99
+
100
+ // Disallows unnecessary path segments, encourages clean and readable
101
+ // paths
102
+ "import/no-useless-path-segments": ["error", { commonjs: true }],
103
+
104
+ // Flags modules with exports that are unused or missing, helps maintain
105
+ // clean and efficient code
106
+ "import/no-unused-modules": [
107
+ "error",
108
+ {
109
+ unusedExports: true,
110
+ missingExports: true,
111
+ },
112
+ ],
113
+
114
+ // Prevents mixing CommonJS-style exports with `import`, ensures
115
+ // consistency in module style
116
+ "import/no-import-module-exports": ["error", { exceptions: [] }],
117
+
118
+ // Disallows imports from sibling packages using relative paths, enforces
119
+ // proper package boundaries
120
+ "import/no-relative-packages": "error",
121
+
122
+ // Standardizes inline `type` specifier placement, improves consistency in
123
+ // type-aware codebases
124
+ "import/consistent-type-specifier-style": ["error", "prefer-inline"],
125
+
126
+ // Prevents empty named import blocks, avoids noise and unnecessary syntax
127
+ "import/no-empty-named-blocks": "error",
128
+ },
129
+ },
130
+ ];
package/rules/jsdoc.js ADDED
@@ -0,0 +1,12 @@
1
+ import jsdoc from "eslint-plugin-jsdoc";
2
+
3
+ /** @type {import('eslint').Linter.Config[]} */
4
+ export default [
5
+ {
6
+ name: "spl/jsdoc",
7
+ files: ["**/*.{js,mjs,cjs}"],
8
+ plugins: {
9
+ jsdoc,
10
+ },
11
+ },
12
+ ];
@@ -0,0 +1,18 @@
1
+ import eslintPluginPrettier from "eslint-plugin-prettier";
2
+
3
+ /** @type {import('eslint').Linter.Config[]} */
4
+ export default [
5
+ {
6
+ name: "spl/prettier",
7
+ plugins: {
8
+ prettier: eslintPluginPrettier,
9
+ },
10
+ rules: {
11
+ "prettier/prettier": "warn",
12
+
13
+ // Rules to disable in favor of prettier
14
+ "prefer-arrow-callback": "off",
15
+ "arrow-body-style": "off",
16
+ },
17
+ },
18
+ ];
package/rules/react.js ADDED
@@ -0,0 +1,397 @@
1
+ import reactPlugin from "eslint-plugin-react";
2
+ import jsxA11y from "eslint-plugin-jsx-a11y";
3
+ import reactHooks from "eslint-plugin-react-hooks";
4
+
5
+ /** @type {import('eslint').Linter.Config[]} */
6
+ export default [
7
+ {
8
+ name: "spl/react",
9
+ files: ["**/*.js"],
10
+ settings: {
11
+ react: {
12
+ version: "detect",
13
+ },
14
+ },
15
+ languageOptions: {
16
+ parserOptions: {
17
+ ecmaFeatures: {
18
+ jsx: true,
19
+ },
20
+ },
21
+ },
22
+ plugins: {
23
+ "react": reactPlugin,
24
+ "jsx-a11y": jsxA11y,
25
+ "react-hooks": reactHooks,
26
+ },
27
+ rules: {
28
+ ...reactPlugin.configs.flat.recommended.rules,
29
+ ...reactPlugin.configs.flat["jsx-runtime"].rules,
30
+ ...jsxA11y.flatConfigs.recommended.rules,
31
+
32
+ // Prevent missing displayName in a React component definition
33
+ "react/display-name": ["off", { ignoreTranspilerName: false }],
34
+
35
+ // Enforce boolean attributes notation in JSX
36
+ "react/jsx-boolean-value": ["error", "never", { always: [] }],
37
+
38
+ // Validate closing bracket location in JSX
39
+ "react/jsx-closing-bracket-location": ["error", "tag-aligned"],
40
+
41
+ // Validate closing tag location in JSX
42
+ "react/jsx-closing-tag-location": "error",
43
+
44
+ // Enforce or disallow spaces inside of curly braces in JSX attributes
45
+ "react/jsx-curly-spacing": [
46
+ "error",
47
+ { when: "never", allowMultiline: true },
48
+ ],
49
+
50
+ // Enforce or disallow spaces around equal sign
51
+ "react/jsx-equals-spacing": ["error", "always"],
52
+
53
+ // Disable event handler naming conventions in JSX
54
+ "react/jsx-handler-names": "off",
55
+
56
+ // Validate props indentation in JSX
57
+ "react/jsx-indent-props": ["error", 2],
58
+
59
+ // Limit maximum of props on a single line in JSX
60
+ "react/jsx-max-props-per-line": [
61
+ "error",
62
+ { maximum: 1, when: "multiline" },
63
+ ],
64
+
65
+ // Prevent usage of .bind() in JSX props
66
+ "react/jsx-no-bind": [
67
+ "error",
68
+ {
69
+ ignoreRefs: true,
70
+ allowArrowFunctions: true,
71
+ allowFunctions: false,
72
+ allowBind: false,
73
+ ignoreDOMComponents: true,
74
+ },
75
+ ],
76
+
77
+ // Prevent usage of unwrapped JSX strings
78
+ "react/jsx-no-literals": "off",
79
+
80
+ // Enforce PascalCase for JSX components
81
+ "react/jsx-pascal-case": ["error", { allowAllCaps: true }],
82
+
83
+ // Disable props sorting
84
+ "react/jsx-sort-props": "off",
85
+
86
+ // [Deprecated] Disable defaultProps declarations sorting
87
+ "react/jsx-sort-default-props": "off",
88
+
89
+ // No jsx extension
90
+ // https://github.com/facebook/create-react-app/issues/87#issuecomment-234627904
91
+ "react/jsx-filename-extension": ["error", { extensions: [".js"] }],
92
+
93
+ // This is no longer needed since React 17+ with the new JSX transform
94
+ // https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
95
+ "react/react-in-jsx-scope": "off",
96
+
97
+ // React 18.3.0 deprecated defaultProps for function components
98
+ // https://github.com/facebook/react/pull/25699
99
+ "react/require-default-props": "off",
100
+
101
+ // We recommend using TypeScript instead of checking prop types at runtime
102
+ // https://react.dev/reference/react/Component#static-proptypes
103
+ "react/prop-types": "off",
104
+
105
+ // Enforce arrow function type for function components, ensures
106
+ // consistency and avoids confusion between class and function components
107
+ "react/function-component-definition": [
108
+ "error",
109
+ {
110
+ namedComponents: "arrow-function",
111
+ unnamedComponents: "arrow-function",
112
+ },
113
+ ],
114
+
115
+ // Allow prop spreading to enable flexible and reusable components
116
+ "react/jsx-props-no-spreading": "off",
117
+
118
+ // Allow unescaped entities in JSX (e.g., apostrophes), prevents noise for
119
+ // common text content in JSX
120
+ "react/no-unescaped-entities": "off",
121
+
122
+ // Allow use of `dangerouslySetInnerHTML`, needed in some cases and
123
+ // the responsibility lies with the developer
124
+ "react/no-danger": "off",
125
+
126
+ // Prevent usage of deprecated methods
127
+ "react/no-deprecated": "error",
128
+
129
+ // Prevent multiple component definition per file
130
+ "react/no-multi-comp": "off",
131
+
132
+ // Prevent usage of setState
133
+ "react/no-set-state": "off",
134
+
135
+ // Require ES6 class declarations over React.createClass
136
+ "react/prefer-es6-class": ["error", "always"],
137
+
138
+ // Enforce that all elements that require alternative text have meaningful
139
+ // information
140
+ "jsx-a11y/alt-text": [
141
+ "error",
142
+ {
143
+ "elements": ["img", "object", "area", 'input[type="image"]'],
144
+ "img": [],
145
+ "object": [],
146
+ "area": [],
147
+ 'input[type="image"]': [],
148
+ },
149
+ ],
150
+
151
+ // Enforce that anchors have content
152
+ "jsx-a11y/anchor-has-content": ["error", { components: [] }],
153
+
154
+ // Ensure <a> tags are valid
155
+ // https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/0745af376cdc8686d85a361ce36952b1fb1ccf6e/docs/rules/anchor-is-valid.md
156
+ "jsx-a11y/anchor-is-valid": [
157
+ "error",
158
+ {
159
+ components: ["Link"],
160
+ specialLink: ["to"],
161
+ aspects: ["noHref", "invalidHref", "preferButton"],
162
+ },
163
+ ],
164
+
165
+ // Elements with aria-activedescendant must be tabbable
166
+ "jsx-a11y/aria-activedescendant-has-tabindex": "error",
167
+
168
+ // Enforce all aria-* props are valid
169
+ "jsx-a11y/aria-props": "error",
170
+
171
+ // Enforce ARIA state and property values are valid
172
+ "jsx-a11y/aria-proptypes": "error",
173
+
174
+ // Require ARIA roles to be valid and non-abstract
175
+ "jsx-a11y/aria-role": ["error", { ignoreNonDOM: false }],
176
+
177
+ // Enforce that elements that do not support ARIA roles, states, and
178
+ // properties do not have those attributes
179
+ "jsx-a11y/aria-unsupported-elements": "error",
180
+
181
+ // Ensure the autocomplete attribute is correct and suitable for the form
182
+ // field it is used with
183
+ "jsx-a11y/autocomplete-valid": [
184
+ "off",
185
+ {
186
+ inputComponents: [],
187
+ },
188
+ ],
189
+
190
+ // Require onClick be accompanied by onKeyUp/onKeyDown/onKeyPress
191
+ "jsx-a11y/click-events-have-key-events": "error",
192
+
193
+ // Enforce that a control (an interactive element) has a text label
194
+ "jsx-a11y/control-has-associated-label": [
195
+ "error",
196
+ {
197
+ labelAttributes: ["label"],
198
+ controlComponents: [],
199
+ ignoreElements: [
200
+ "audio",
201
+ "canvas",
202
+ "embed",
203
+ "input",
204
+ "textarea",
205
+ "tr",
206
+ "video",
207
+ ],
208
+ ignoreRoles: [
209
+ "grid",
210
+ "listbox",
211
+ "menu",
212
+ "menubar",
213
+ "radiogroup",
214
+ "row",
215
+ "tablist",
216
+ "toolbar",
217
+ "tree",
218
+ "treegrid",
219
+ ],
220
+ depth: 5,
221
+ },
222
+ ],
223
+
224
+ // Ensure <hX> tags have content and are not aria-hidden
225
+ "jsx-a11y/heading-has-content": ["error", { components: [""] }],
226
+
227
+ // Require HTML elements to have a "lang" prop
228
+ "jsx-a11y/html-has-lang": "error",
229
+
230
+ // Ensure iframe elements have a unique title
231
+ "jsx-a11y/iframe-has-title": "error",
232
+
233
+ // Prevent img alt text from containing redundant words like "image",
234
+ // "picture", or "photo"
235
+ "jsx-a11y/img-redundant-alt": "error",
236
+
237
+ // Elements with an interactive role and interaction handlers must be
238
+ // focusable
239
+ "jsx-a11y/interactive-supports-focus": "error",
240
+
241
+ // Enforce that a label tag has a text label and an associated control
242
+ "jsx-a11y/label-has-associated-control": [
243
+ "error",
244
+ {
245
+ labelComponents: [],
246
+ labelAttributes: [],
247
+ controlComponents: [],
248
+ assert: "both",
249
+ depth: 25,
250
+ },
251
+ ],
252
+
253
+ // Require HTML element's lang prop to be valid
254
+ "jsx-a11y/lang": "error",
255
+
256
+ // Media elements must have captions
257
+ "jsx-a11y/media-has-caption": [
258
+ "error",
259
+ {
260
+ audio: [],
261
+ video: [],
262
+ track: [],
263
+ },
264
+ ],
265
+
266
+ // Require that mouseover/out come with focus/blur, for keyboard-only
267
+ // users
268
+ "jsx-a11y/mouse-events-have-key-events": "error",
269
+
270
+ // Prevent use of `accessKey`
271
+ "jsx-a11y/no-access-key": "error",
272
+
273
+ // Prohibit autoFocus prop
274
+ "jsx-a11y/no-autofocus": ["error", { ignoreNonDOM: true }],
275
+
276
+ // Prevent distracting elements, like <marquee> and <blink>
277
+ "jsx-a11y/no-distracting-elements": [
278
+ "error",
279
+ {
280
+ elements: ["marquee", "blink"],
281
+ },
282
+ ],
283
+
284
+ // WAI-ARIA roles should not be used to convert an interactive element to
285
+ // non-interactive
286
+ "jsx-a11y/no-interactive-element-to-noninteractive-role": [
287
+ "error",
288
+ {
289
+ tr: ["none", "presentation"],
290
+ },
291
+ ],
292
+
293
+ // A non-interactive element does not support event handlers (mouse and
294
+ // key handlers)
295
+ "jsx-a11y/no-noninteractive-element-interactions": [
296
+ "error",
297
+ {
298
+ handlers: [
299
+ "onClick",
300
+ "onMouseDown",
301
+ "onMouseUp",
302
+ "onKeyPress",
303
+ "onKeyDown",
304
+ "onKeyUp",
305
+ ],
306
+ },
307
+ ],
308
+
309
+ // WAI-ARIA roles should not be used to convert a non-interactive element
310
+ // to interactive
311
+ "jsx-a11y/no-noninteractive-element-to-interactive-role": [
312
+ "error",
313
+ {
314
+ ul: [
315
+ "listbox",
316
+ "menu",
317
+ "menubar",
318
+ "radiogroup",
319
+ "tablist",
320
+ "tree",
321
+ "treegrid",
322
+ ],
323
+ ol: [
324
+ "listbox",
325
+ "menu",
326
+ "menubar",
327
+ "radiogroup",
328
+ "tablist",
329
+ "tree",
330
+ "treegrid",
331
+ ],
332
+ li: ["menuitem", "option", "row", "tab", "treeitem"],
333
+ table: ["grid"],
334
+ td: ["gridcell"],
335
+ },
336
+ ],
337
+
338
+ // Tab key navigation should be limited to elements on the page that can
339
+ // be interacted with
340
+ "jsx-a11y/no-noninteractive-tabindex": [
341
+ "error",
342
+ {
343
+ tags: [],
344
+ roles: ["tabpanel"],
345
+ allowExpressionValues: true,
346
+ },
347
+ ],
348
+
349
+ // [Deprecated] Require onBlur instead of onChange
350
+ "jsx-a11y/no-onchange": "off",
351
+
352
+ // Ensure HTML elements do not specify redundant ARIA roles
353
+ "jsx-a11y/no-redundant-roles": [
354
+ "error",
355
+ {
356
+ nav: ["navigation"],
357
+ },
358
+ ],
359
+
360
+ // Enforce that DOM elements without semantic behavior not have
361
+ // interaction handlers
362
+ "jsx-a11y/no-static-element-interactions": [
363
+ "error",
364
+ {
365
+ handlers: [
366
+ "onClick",
367
+ "onMouseDown",
368
+ "onMouseUp",
369
+ "onKeyPress",
370
+ "onKeyDown",
371
+ "onKeyUp",
372
+ ],
373
+ },
374
+ ],
375
+
376
+ // Enforce that elements with ARIA roles must have all required attributes
377
+ // for that role
378
+ "jsx-a11y/role-has-required-aria-props": "error",
379
+
380
+ // Enforce that elements with explicit or implicit roles defined contain
381
+ // only aria-* properties supported by that role
382
+ "jsx-a11y/role-supports-aria-props": "error",
383
+
384
+ // Only allow <th> to have the "scope" attr
385
+ "jsx-a11y/scope": "error",
386
+
387
+ // Enforce tabIndex value is not greater than zero
388
+ "jsx-a11y/tabindex-no-positive": "error",
389
+
390
+ // Enforce Rules of Hooks
391
+ "react-hooks/rules-of-hooks": "error",
392
+
393
+ // Verify the list of the dependencies for Hooks like useEffect
394
+ "react-hooks/exhaustive-deps": "warn",
395
+ },
396
+ },
397
+ ];