js-style-kit 0.6.1 → 0.7.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.
Files changed (64) hide show
  1. package/README.md +5 -0
  2. package/dist/index.d.ts +11 -5
  3. package/dist/index.js +48 -7
  4. package/dist/index.js.map +1 -1
  5. package/package.json +8 -5
  6. package/src/eslint/base/README.md +186 -0
  7. package/src/eslint/base/config.ts +37 -0
  8. package/src/eslint/base/rules.ts +444 -0
  9. package/src/eslint/base/types.ts +20 -0
  10. package/src/eslint/constants.ts +52 -0
  11. package/src/eslint/convex/README.md +30 -0
  12. package/src/eslint/convex/config.ts +34 -0
  13. package/src/eslint/convex/rules.ts +8 -0
  14. package/src/eslint/convex/types.ts +8 -0
  15. package/src/eslint/ignores.ts +31 -0
  16. package/src/eslint/import/README.md +397 -0
  17. package/src/eslint/import/config.ts +48 -0
  18. package/src/eslint/import/rules.ts +81 -0
  19. package/src/eslint/index.ts +259 -0
  20. package/src/eslint/jsdoc/README.md +399 -0
  21. package/src/eslint/jsdoc/config.ts +29 -0
  22. package/src/eslint/jsdoc/rules.ts +81 -0
  23. package/src/eslint/jsdoc/types.ts +56 -0
  24. package/src/eslint/nextjs/config.ts +25 -0
  25. package/src/eslint/nextjs/rules.ts +25 -0
  26. package/src/eslint/nextjs/types.ts +27 -0
  27. package/src/eslint/perfectionist/README.md +454 -0
  28. package/src/eslint/perfectionist/config.ts +25 -0
  29. package/src/eslint/perfectionist/rules.ts +39 -0
  30. package/src/eslint/prefer-arrow-function/config.ts +33 -0
  31. package/src/eslint/prefer-arrow-function/types.ts +13 -0
  32. package/src/eslint/process-custom-rules.ts +72 -0
  33. package/src/eslint/query/README.md +254 -0
  34. package/src/eslint/query/config.ts +27 -0
  35. package/src/eslint/query/rules.ts +11 -0
  36. package/src/eslint/query/types.ts +11 -0
  37. package/src/eslint/react/README.md +416 -0
  38. package/src/eslint/react/config.ts +65 -0
  39. package/src/eslint/react/rules.ts +188 -0
  40. package/src/eslint/react/types.ts +26 -0
  41. package/src/eslint/react-refresh/config.ts +28 -0
  42. package/src/eslint/react-refresh/rules.ts +48 -0
  43. package/src/eslint/storybook/README.md +424 -0
  44. package/src/eslint/storybook/config.ts +57 -0
  45. package/src/eslint/testing/README.md +436 -0
  46. package/src/eslint/testing/config.ts +90 -0
  47. package/src/eslint/testing/jest-rules.ts +47 -0
  48. package/src/eslint/testing/vitest-rules.ts +42 -0
  49. package/src/eslint/turbo/README.md +380 -0
  50. package/src/eslint/turbo/config.ts +26 -0
  51. package/src/eslint/turbo/types.ts +7 -0
  52. package/src/eslint/types.ts +29 -0
  53. package/src/eslint/typescript/README.md +229 -0
  54. package/src/eslint/typescript/config.ts +48 -0
  55. package/src/eslint/typescript/rules.ts +137 -0
  56. package/src/eslint/typescript/types.ts +35 -0
  57. package/src/eslint/unicorn/README.md +497 -0
  58. package/src/eslint/unicorn/config.ts +36 -0
  59. package/src/eslint/unicorn/rules.ts +86 -0
  60. package/src/index.ts +3 -0
  61. package/src/modules.d.ts +5 -0
  62. package/src/prettier/README.md +413 -0
  63. package/src/prettier/index.ts +110 -0
  64. package/src/utils/is-type.ts +60 -0
@@ -0,0 +1,137 @@
1
+ import type { TypescriptRules } from "./types.js";
2
+
3
+ export const tseslintRules: TypescriptRules = {
4
+ "@typescript-eslint/adjacent-overload-signatures": "warn",
5
+ "@typescript-eslint/array-type": "warn",
6
+ "@typescript-eslint/await-thenable": "warn",
7
+ "@typescript-eslint/ban-ts-comment": [
8
+ "warn",
9
+ { minimumDescriptionLength: 10 },
10
+ ],
11
+ "@typescript-eslint/ban-tslint-comment": "warn",
12
+ "@typescript-eslint/class-literal-property-style": "warn",
13
+ "@typescript-eslint/consistent-generic-constructors": "warn",
14
+ "@typescript-eslint/consistent-indexed-object-style": "warn",
15
+ "@typescript-eslint/consistent-type-assertions": "warn",
16
+ "@typescript-eslint/consistent-type-definitions": "warn",
17
+ "@typescript-eslint/consistent-type-exports": [
18
+ "warn",
19
+ { fixMixedExportsWithInlineTypeSpecifier: true },
20
+ ],
21
+ "@typescript-eslint/consistent-type-imports": [
22
+ "warn",
23
+ {
24
+ fixStyle: "inline-type-imports",
25
+ prefer: "type-imports",
26
+ },
27
+ ],
28
+ "@typescript-eslint/dot-notation": "warn",
29
+ "@typescript-eslint/no-array-constructor": "warn",
30
+ "@typescript-eslint/no-array-delete": "warn",
31
+ "@typescript-eslint/no-base-to-string": "warn",
32
+ "@typescript-eslint/no-confusing-non-null-assertion": "warn",
33
+ "@typescript-eslint/no-confusing-void-expression": "warn",
34
+ "@typescript-eslint/no-deprecated": "warn",
35
+ "@typescript-eslint/no-duplicate-enum-values": "warn",
36
+ "@typescript-eslint/no-duplicate-type-constituents": "warn",
37
+ "@typescript-eslint/no-dynamic-delete": "warn",
38
+ "@typescript-eslint/no-empty-function": "warn",
39
+ "@typescript-eslint/no-empty-object-type": "warn",
40
+ "@typescript-eslint/no-explicit-any": "warn",
41
+ "@typescript-eslint/no-extra-non-null-assertion": "warn",
42
+ "@typescript-eslint/no-extraneous-class": "warn",
43
+ "@typescript-eslint/no-floating-promises": "warn",
44
+ "@typescript-eslint/no-for-in-array": "warn",
45
+ "@typescript-eslint/no-implied-eval": "warn",
46
+ "@typescript-eslint/no-import-type-side-effects": "warn",
47
+ "@typescript-eslint/no-inferrable-types": "warn",
48
+ "@typescript-eslint/no-invalid-void-type": "warn",
49
+ "@typescript-eslint/no-meaningless-void-operator": "warn",
50
+ "@typescript-eslint/no-misused-new": "warn",
51
+ "@typescript-eslint/no-misused-promises": "warn",
52
+ "@typescript-eslint/no-mixed-enums": "warn",
53
+ "@typescript-eslint/no-namespace": "warn",
54
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "warn",
55
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "warn",
56
+ "@typescript-eslint/no-non-null-assertion": "warn",
57
+ "@typescript-eslint/no-redundant-type-constituents": "warn",
58
+ "@typescript-eslint/no-require-imports": "warn",
59
+ "@typescript-eslint/no-this-alias": "warn",
60
+ "@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn",
61
+ "@typescript-eslint/no-unnecessary-condition": "warn",
62
+ "@typescript-eslint/no-unnecessary-template-expression": "warn",
63
+ "@typescript-eslint/no-unnecessary-type-arguments": "warn",
64
+ "@typescript-eslint/no-unnecessary-type-assertion": "warn",
65
+ "@typescript-eslint/no-unnecessary-type-constraint": "warn",
66
+ "@typescript-eslint/no-unnecessary-type-parameters": "warn",
67
+ "@typescript-eslint/no-unsafe-declaration-merging": "warn",
68
+ "@typescript-eslint/no-unsafe-enum-comparison": "warn",
69
+ "@typescript-eslint/no-unsafe-function-type": "warn",
70
+ "@typescript-eslint/no-unsafe-unary-minus": "warn",
71
+ "@typescript-eslint/no-unused-expressions": "warn",
72
+ "@typescript-eslint/no-unused-vars": [
73
+ "warn",
74
+ {
75
+ args: "after-used",
76
+ argsIgnorePattern: "^_",
77
+ ignoreRestSiblings: false,
78
+ vars: "all",
79
+ varsIgnorePattern: "^_",
80
+ },
81
+ ],
82
+ "@typescript-eslint/no-useless-constructor": "warn",
83
+ "@typescript-eslint/no-wrapper-object-types": "warn",
84
+ // TODO: Investiate non-null vs type-cast rules
85
+ "@typescript-eslint/non-nullable-type-assertion-style": "off",
86
+ "@typescript-eslint/only-throw-error": "warn",
87
+ "@typescript-eslint/prefer-as-const": "warn",
88
+ "@typescript-eslint/prefer-find": "warn",
89
+ "@typescript-eslint/prefer-for-of": "warn",
90
+ "@typescript-eslint/prefer-function-type": "warn",
91
+ "@typescript-eslint/prefer-includes": "warn",
92
+ "@typescript-eslint/prefer-literal-enum-member": "warn",
93
+ "@typescript-eslint/prefer-namespace-keyword": "warn",
94
+ "@typescript-eslint/prefer-nullish-coalescing": [
95
+ "warn",
96
+ {
97
+ ignorePrimitives: { string: true },
98
+ },
99
+ ],
100
+ "@typescript-eslint/prefer-optional-chain": "warn",
101
+ "@typescript-eslint/prefer-promise-reject-errors": "warn",
102
+ "@typescript-eslint/prefer-reduce-type-parameter": "warn",
103
+ "@typescript-eslint/prefer-regexp-exec": "warn",
104
+ "@typescript-eslint/prefer-return-this-type": "warn",
105
+ "@typescript-eslint/prefer-string-starts-ends-with": "warn",
106
+ "@typescript-eslint/related-getter-setter-pairs": "warn",
107
+ "@typescript-eslint/require-await": "warn",
108
+ "@typescript-eslint/restrict-plus-operands": [
109
+ "warn",
110
+ {
111
+ allowAny: false,
112
+ allowBoolean: false,
113
+ allowNullish: false,
114
+ allowNumberAndString: false,
115
+ allowRegExp: false,
116
+ },
117
+ ],
118
+ "@typescript-eslint/restrict-template-expressions": [
119
+ "warn",
120
+ {
121
+ allow: [{ from: "lib", name: ["Error", "URL", "URLSearchParams"] }],
122
+ allowAny: true,
123
+ allowBoolean: true,
124
+ allowNullish: true,
125
+ allowNumber: true,
126
+ allowRegExp: true,
127
+ },
128
+ ],
129
+ "@typescript-eslint/return-await": [
130
+ "warn",
131
+ "error-handling-correctness-only",
132
+ ],
133
+ "@typescript-eslint/triple-slash-reference": "warn",
134
+ "@typescript-eslint/unbound-method": "warn",
135
+ "@typescript-eslint/unified-signatures": "warn",
136
+ "@typescript-eslint/use-unknown-in-catch-callback-variable": "warn",
137
+ };
@@ -0,0 +1,35 @@
1
+ import type { EslintRuleConfig } from "../types.js";
2
+
3
+ export type TypescriptRules = Record<
4
+ `@typescript-eslint/${string}`,
5
+ EslintRuleConfig
6
+ > & {
7
+ "@typescript-eslint/prefer-nullish-coalescing": EslintRuleConfig<{
8
+ /** Unless this is set to `true`, the rule will error on every file whose `tsconfig.json` does _not_ have the `strictNullChecks` compiler option (or `strict`) set to `true`. */
9
+ allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing?: boolean;
10
+ /** Whether to ignore arguments to the `Boolean` constructor */
11
+ ignoreBooleanCoercion?: boolean;
12
+ /** Whether to ignore cases that are located within a conditional test. */
13
+ ignoreConditionalTests?: boolean;
14
+ /** Whether to ignore any if statements that could be simplified by using the nullish coalescing operator. */
15
+ ignoreIfStatements?: boolean;
16
+ /** Whether to ignore any logical or expressions that are part of a mixed logical expression (with `&&`). */
17
+ ignoreMixedLogicalExpressions?: boolean;
18
+ /** Whether to ignore all (`true`) or some (an object with properties) primitive types. */
19
+ ignorePrimitives?:
20
+ | true
21
+ | {
22
+ [k: string]: unknown;
23
+ /** Ignore bigint primitive types. */
24
+ bigint?: boolean;
25
+ /** Ignore boolean primitive types. */
26
+ boolean?: boolean;
27
+ /** Ignore number primitive types. */
28
+ number?: boolean;
29
+ /** Ignore string primitive types. */
30
+ string?: boolean;
31
+ };
32
+ /** Whether to ignore any ternary expressions that could be simplified by using the nullish coalescing operator. */
33
+ ignoreTernaryTests?: boolean;
34
+ }>;
35
+ };
@@ -0,0 +1,497 @@
1
+ # Unicorn Configuration
2
+
3
+ Modern JavaScript/TypeScript best practices and code quality rules powered by `eslint-plugin-unicorn`.
4
+
5
+ [← Back to main README](../../../README.md)
6
+
7
+ ## Overview
8
+
9
+ Unicorn configuration is **enabled by default** and provides:
10
+
11
+ - File naming conventions (kebab-case)
12
+ - Modern JavaScript best practices
13
+ - Error handling improvements
14
+ - Node.js protocol enforcement
15
+ - String and regex optimizations
16
+
17
+ ## Quick Start
18
+
19
+ The unicorn configuration is automatically enabled when you use `eslintConfig()`:
20
+
21
+ ```js
22
+ import { eslintConfig } from "js-style-kit";
23
+
24
+ export default eslintConfig(); // Unicorn rules enabled by default with kebab-case filenames
25
+ ```
26
+
27
+ ### Configure Filename Case
28
+
29
+ You can customize the filename case convention:
30
+
31
+ ```js
32
+ import { eslintConfig } from "js-style-kit";
33
+
34
+ export default eslintConfig({
35
+ unicorn: {
36
+ filenameCase: "camelCase", // "camelCase" | "kebabCase" | "pascalCase" | "snakeCase"
37
+ },
38
+ });
39
+ ```
40
+
41
+ ## Key Features
42
+
43
+ ### File Naming
44
+
45
+ Enforces consistent filename case conventions. By default, kebab-case is used, which promotes consistency and avoids case-sensitivity issues across different operating systems.
46
+
47
+ **Available Options:**
48
+
49
+ - `kebabCase` (default) - `user-service.ts`, `api-client.js`
50
+ - `camelCase` - `userService.ts`, `apiClient.js`
51
+ - `pascalCase` - `UserService.ts`, `ApiClient.js`
52
+ - `snakeCase` - `user_service.ts`, `api_client.js`
53
+
54
+ ```
55
+ ✅ Good filenames:
56
+ - user-service.ts
57
+ - api-client.js
58
+ - button-component.tsx
59
+ - use-auth-hook.ts
60
+ - format-date.util.ts
61
+
62
+ ❌ Bad filenames:
63
+ - UserService.ts (PascalCase)
64
+ - apiClient.js (camelCase)
65
+ - Button_Component.tsx (snake_case)
66
+ - useAuthHook.ts (camelCase)
67
+ ```
68
+
69
+ **Rule:**
70
+
71
+ - `unicorn/filename-case` - Enforces consistent filename case
72
+
73
+ **Configuration:**
74
+
75
+ ```js
76
+ // Use camelCase for all files
77
+ export default eslintConfig({
78
+ unicorn: {
79
+ filenameCase: "camelCase",
80
+ },
81
+ });
82
+
83
+ // Use PascalCase for all files
84
+ export default eslintConfig({
85
+ unicorn: {
86
+ filenameCase: "pascalCase",
87
+ },
88
+ });
89
+ ```
90
+
91
+ For more advanced customization (like ignoring specific patterns), you can override the rule directly:
92
+
93
+ ```js
94
+ export default eslintConfig({
95
+ unicorn: {
96
+ filenameCase: "kebabCase",
97
+ },
98
+ rules: {
99
+ // Allow PascalCase for React components
100
+ "unicorn/filename-case": [
101
+ "warn",
102
+ {
103
+ case: "kebabCase",
104
+ ignore: [
105
+ "^[A-Z].*\\.tsx$", // Allow PascalCase for .tsx files
106
+ ],
107
+ },
108
+ ],
109
+ },
110
+ });
111
+ ```
112
+
113
+ ### Node.js Protocol
114
+
115
+ Requires using the `node:` protocol when importing Node.js built-in modules:
116
+
117
+ ```js
118
+ // ✅ Good - node: protocol
119
+ import { readFile } from "node:fs/promises";
120
+ import { join } from "node:path";
121
+ import { EventEmitter } from "node:events";
122
+
123
+ // ❌ Bad - missing node: protocol
124
+ import { readFile } from "fs/promises";
125
+ import { join } from "path";
126
+ import { EventEmitter } from "events";
127
+ ```
128
+
129
+ **Rule:**
130
+
131
+ - `unicorn/prefer-node-protocol` - Enforces `node:` prefix (auto-fixable)
132
+
133
+ **Benefits:**
134
+
135
+ - Makes it clear which modules are built-in
136
+ - Prevents conflicts with npm packages
137
+ - Improves module resolution performance
138
+
139
+ ### Error Handling
140
+
141
+ Ensures errors have meaningful messages:
142
+
143
+ ```js
144
+ // ✅ Good - descriptive error messages
145
+ throw new Error("Failed to fetch user data");
146
+ throw new TypeError("Expected string, got number");
147
+
148
+ // ❌ Bad - empty error
149
+ throw new Error();
150
+ throw new TypeError();
151
+ ```
152
+
153
+ **Rules:**
154
+
155
+ - `unicorn/error-message` - Requires error messages
156
+ - `unicorn/prefer-type-error` - Use `TypeError` for type checking (auto-fixable)
157
+
158
+ ### Better Regex
159
+
160
+ Optimizes regular expressions:
161
+
162
+ ```js
163
+ // ✅ Good - optimized regex
164
+ const regex = /\d+/;
165
+ const pattern = /[a-z]+/i;
166
+
167
+ // ❌ Bad - can be optimized
168
+ const regex = /[0-9]+/; // Use \d instead
169
+ const pattern = /[a-zA-Z]+/; // Use /[a-z]+/i instead
170
+ ```
171
+
172
+ **Rule:**
173
+
174
+ - `unicorn/better-regex` - Suggests regex improvements (auto-fixable)
175
+
176
+ ### Modern JavaScript
177
+
178
+ Promotes modern language features:
179
+
180
+ #### String Methods
181
+
182
+ ```js
183
+ // ✅ Good - replaceAll for global replacement
184
+ const text = "hello hello".replaceAll("hello", "hi");
185
+
186
+ // ❌ Bad - global regex for simple replacement
187
+ const text = "hello hello".replace(/hello/g, "hi");
188
+ ```
189
+
190
+ **Rule:**
191
+
192
+ - `unicorn/prefer-string-replace-all` - Use `replaceAll()` over global regex (auto-fixable)
193
+
194
+ #### Loop Improvements
195
+
196
+ ```js
197
+ // ✅ Good - for...of loop
198
+ for (const item of items) {
199
+ process(item);
200
+ }
201
+
202
+ // ❌ Bad - traditional for loop for simple iteration
203
+ for (let i = 0; i < items.length; i++) {
204
+ process(items[i]);
205
+ }
206
+ ```
207
+
208
+ **Rule:**
209
+
210
+ - `unicorn/no-for-loop` - Prefer for...of over C-style for loops (auto-fixable)
211
+
212
+ #### Built-in Constructors
213
+
214
+ ```js
215
+ // ✅ Good - using new with constructors
216
+ const map = new Map();
217
+ const set = new Set();
218
+ const date = new Date();
219
+
220
+ // ❌ Bad - missing new
221
+ const map = Map();
222
+ const set = Set();
223
+ ```
224
+
225
+ **Rule:**
226
+
227
+ - `unicorn/new-for-builtins` - Enforces `new` for built-ins (auto-fixable)
228
+
229
+ ### Event Listeners
230
+
231
+ Promotes modern event handling:
232
+
233
+ ```js
234
+ // ✅ Good - addEventListener
235
+ element.addEventListener("click", handler);
236
+
237
+ // ❌ Bad - on-handler
238
+ element.onclick = handler;
239
+ ```
240
+
241
+ **Rule:**
242
+
243
+ - `unicorn/prefer-add-event-listener` - Use `addEventListener()` (auto-fixable)
244
+
245
+ ### Switch Case Braces
246
+
247
+ Enforces consistent brace usage in switch cases:
248
+
249
+ ```js
250
+ // ✅ Good - braces around case
251
+ switch (value) {
252
+ case "a": {
253
+ const result = doSomething();
254
+ return result;
255
+ }
256
+ case "b": {
257
+ return doOther();
258
+ }
259
+ default: {
260
+ return defaultValue;
261
+ }
262
+ }
263
+
264
+ // ❌ Bad - missing braces
265
+ switch (value) {
266
+ case "a":
267
+ const result = doSomething(); // Variable could leak to other cases
268
+ return result;
269
+ }
270
+ ```
271
+
272
+ **Rule:**
273
+
274
+ - `unicorn/switch-case-braces` - Always use braces in case clauses (auto-fixable)
275
+
276
+ ### Text Encoding
277
+
278
+ Enforces consistent encoding identifier case:
279
+
280
+ ```js
281
+ // ✅ Good - lowercase encoding
282
+ const text = new TextDecoder("utf-8").decode(buffer);
283
+ const encoded = new TextEncoder().encode("text");
284
+
285
+ // ❌ Bad - inconsistent case
286
+ const text = new TextDecoder("UTF-8").decode(buffer);
287
+ ```
288
+
289
+ **Rule:**
290
+
291
+ - `unicorn/text-encoding-identifier-case` - Lowercase encoding identifiers (auto-fixable)
292
+
293
+ ### Console Improvements
294
+
295
+ Prevents extra spaces in console output:
296
+
297
+ ```js
298
+ // ✅ Good
299
+ console.log("User:", user);
300
+ console.error("Error:", error);
301
+
302
+ // ❌ Bad - extra spaces
303
+ console.log("User: ", user);
304
+ console.error("Error: ", error);
305
+ ```
306
+
307
+ **Rule:**
308
+
309
+ - `unicorn/no-console-spaces` - No spaces before arguments (auto-fixable)
310
+
311
+ ## Complete Example
312
+
313
+ ```ts
314
+ // ✅ user-service.ts - follows all Unicorn rules
315
+ import { readFile } from "node:fs/promises";
316
+ import { join } from "node:path";
317
+
318
+ export class UserService {
319
+ async loadUser(userId: string): Promise<User> {
320
+ if (typeof userId !== "string") {
321
+ throw new TypeError("userId must be a string");
322
+ }
323
+
324
+ const filePath = join("data", "users", `${userId}.json`);
325
+
326
+ try {
327
+ const content = await readFile(filePath, "utf-8");
328
+ return JSON.parse(content);
329
+ } catch (error) {
330
+ throw new Error(`Failed to load user ${userId}`);
331
+ }
332
+ }
333
+
334
+ processUsers(users: User[]): void {
335
+ for (const user of users) {
336
+ console.log("Processing:", user.name);
337
+
338
+ switch (user.status) {
339
+ case "active": {
340
+ this.activateUser(user);
341
+ break;
342
+ }
343
+ case "inactive": {
344
+ this.deactivateUser(user);
345
+ break;
346
+ }
347
+ default: {
348
+ throw new Error(`Unknown status: ${user.status}`);
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+ ```
355
+
356
+ ## Customization
357
+
358
+ ### Change Filename Convention
359
+
360
+ ```js
361
+ // Use camelCase instead of kebab-case
362
+ export default eslintConfig({
363
+ unicorn: {
364
+ filenameCase: "camelCase",
365
+ },
366
+ });
367
+
368
+ // Use PascalCase for component files
369
+ export default eslintConfig({
370
+ unicorn: {
371
+ filenameCase: "pascalCase",
372
+ },
373
+ });
374
+ ```
375
+
376
+ ### Override Filename Convention with Patterns
377
+
378
+ ```js
379
+ export default eslintConfig({
380
+ unicorn: {
381
+ filenameCase: "kebabCase",
382
+ },
383
+ rules: {
384
+ // Allow PascalCase for React components
385
+ "unicorn/filename-case": [
386
+ "warn",
387
+ {
388
+ case: "kebabCase",
389
+ ignore: [
390
+ "^[A-Z].*\\.tsx$", // Allow PascalCase for .tsx files
391
+ ],
392
+ },
393
+ ],
394
+ },
395
+ });
396
+ ```
397
+
398
+ ### Disable Specific Rules
399
+
400
+ ```js
401
+ export default eslintConfig({
402
+ rules: {
403
+ // Allow traditional for loops
404
+ "unicorn/no-for-loop": "off",
405
+
406
+ // Allow onclick handlers
407
+ "unicorn/prefer-add-event-listener": "off",
408
+ },
409
+ });
410
+ ```
411
+
412
+ ### File-Specific Overrides
413
+
414
+ ```js
415
+ export default eslintConfig({
416
+ overrides: [
417
+ {
418
+ files: ["scripts/**/*.js"],
419
+ rules: {
420
+ // Allow any filename case in scripts
421
+ "unicorn/filename-case": "off",
422
+ },
423
+ },
424
+ ],
425
+ });
426
+ ```
427
+
428
+ ## Common Patterns
429
+
430
+ ### Node.js Project
431
+
432
+ ```ts
433
+ // server.ts
434
+ import { createServer } from "node:http";
435
+ import { readFile } from "node:fs/promises";
436
+ import { join } from "node:path";
437
+
438
+ const server = createServer(async (req, res) => {
439
+ try {
440
+ const file = await readFile(join("public", "index.html"));
441
+ res.writeHead(200, { "Content-Type": "text/html" });
442
+ res.end(file);
443
+ } catch (error) {
444
+ throw new Error("Failed to read file");
445
+ }
446
+ });
447
+ ```
448
+
449
+ ### Frontend Project
450
+
451
+ ```ts
452
+ // api-client.ts
453
+ export class ApiClient {
454
+ async fetchUsers(): Promise<User[]> {
455
+ const response = await fetch("/api/users");
456
+
457
+ if (!response.ok) {
458
+ throw new Error(`Failed to fetch users: ${response.status}`);
459
+ }
460
+
461
+ return response.json();
462
+ }
463
+ }
464
+ ```
465
+
466
+ ## Benefits
467
+
468
+ ### Code Quality
469
+
470
+ - Catches common mistakes
471
+ - Promotes modern JavaScript
472
+ - Consistent patterns
473
+
474
+ ### Maintainability
475
+
476
+ - Standard file naming
477
+ - Clear error messages
478
+ - Readable code patterns
479
+
480
+ ### Performance
481
+
482
+ - Optimized regex
483
+ - Better module resolution
484
+ - Efficient iterations
485
+
486
+ ## Related Configurations
487
+
488
+ - [Base](../base/README.md) - Base ESLint rules
489
+ - [TypeScript](../typescript/README.md) - TypeScript configuration
490
+ - [Import](../import/README.md) - Import validation
491
+
492
+ ## Learn More
493
+
494
+ - [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn)
495
+ - [Node.js Module System](https://nodejs.org/api/esm.html)
496
+ - [Modern JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
497
+ - [Main README](../../../README.md)
@@ -0,0 +1,36 @@
1
+ import unicorn from "eslint-plugin-unicorn";
2
+
3
+ import type {
4
+ EslintConfigObject,
5
+ EslintRuleConfig,
6
+ FilenameCase,
7
+ } from "../types.js";
8
+
9
+ import { configNames } from "../constants.js";
10
+ import { rules } from "./rules.js";
11
+
12
+ /**
13
+ * ESLint configuration for unicorn plugin.
14
+ * This plugin provides a set of rules to enforce consistent code style and catch common errors.
15
+ *
16
+ * @param options - Configuration options for Unicorn.
17
+ * @param options.customRules - Optional custom rules to merge with the default Unicorn rules.
18
+ * @param options.filenameCase - Optional filename case to enforce. Defaults to "kebabCase".
19
+ * @returns ESLint configuration object for Unicorn.
20
+ */
21
+ export const unicornConfig = ({
22
+ customRules,
23
+ filenameCase = "kebabCase",
24
+ }: {
25
+ customRules?: Record<string, EslintRuleConfig>;
26
+ filenameCase?: FilenameCase;
27
+ }): EslintConfigObject => ({
28
+ name: configNames.unicorn,
29
+ plugins: {
30
+ unicorn,
31
+ },
32
+ rules: {
33
+ ...rules(filenameCase),
34
+ ...(customRules ?? {}),
35
+ },
36
+ });