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.
- package/README.md +5 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +48 -7
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
- package/src/eslint/base/README.md +186 -0
- package/src/eslint/base/config.ts +37 -0
- package/src/eslint/base/rules.ts +444 -0
- package/src/eslint/base/types.ts +20 -0
- package/src/eslint/constants.ts +52 -0
- package/src/eslint/convex/README.md +30 -0
- package/src/eslint/convex/config.ts +34 -0
- package/src/eslint/convex/rules.ts +8 -0
- package/src/eslint/convex/types.ts +8 -0
- package/src/eslint/ignores.ts +31 -0
- package/src/eslint/import/README.md +397 -0
- package/src/eslint/import/config.ts +48 -0
- package/src/eslint/import/rules.ts +81 -0
- package/src/eslint/index.ts +259 -0
- package/src/eslint/jsdoc/README.md +399 -0
- package/src/eslint/jsdoc/config.ts +29 -0
- package/src/eslint/jsdoc/rules.ts +81 -0
- package/src/eslint/jsdoc/types.ts +56 -0
- package/src/eslint/nextjs/config.ts +25 -0
- package/src/eslint/nextjs/rules.ts +25 -0
- package/src/eslint/nextjs/types.ts +27 -0
- package/src/eslint/perfectionist/README.md +454 -0
- package/src/eslint/perfectionist/config.ts +25 -0
- package/src/eslint/perfectionist/rules.ts +39 -0
- package/src/eslint/prefer-arrow-function/config.ts +33 -0
- package/src/eslint/prefer-arrow-function/types.ts +13 -0
- package/src/eslint/process-custom-rules.ts +72 -0
- package/src/eslint/query/README.md +254 -0
- package/src/eslint/query/config.ts +27 -0
- package/src/eslint/query/rules.ts +11 -0
- package/src/eslint/query/types.ts +11 -0
- package/src/eslint/react/README.md +416 -0
- package/src/eslint/react/config.ts +65 -0
- package/src/eslint/react/rules.ts +188 -0
- package/src/eslint/react/types.ts +26 -0
- package/src/eslint/react-refresh/config.ts +28 -0
- package/src/eslint/react-refresh/rules.ts +48 -0
- package/src/eslint/storybook/README.md +424 -0
- package/src/eslint/storybook/config.ts +57 -0
- package/src/eslint/testing/README.md +436 -0
- package/src/eslint/testing/config.ts +90 -0
- package/src/eslint/testing/jest-rules.ts +47 -0
- package/src/eslint/testing/vitest-rules.ts +42 -0
- package/src/eslint/turbo/README.md +380 -0
- package/src/eslint/turbo/config.ts +26 -0
- package/src/eslint/turbo/types.ts +7 -0
- package/src/eslint/types.ts +29 -0
- package/src/eslint/typescript/README.md +229 -0
- package/src/eslint/typescript/config.ts +48 -0
- package/src/eslint/typescript/rules.ts +137 -0
- package/src/eslint/typescript/types.ts +35 -0
- package/src/eslint/unicorn/README.md +497 -0
- package/src/eslint/unicorn/config.ts +36 -0
- package/src/eslint/unicorn/rules.ts +86 -0
- package/src/index.ts +3 -0
- package/src/modules.d.ts +5 -0
- package/src/prettier/README.md +413 -0
- package/src/prettier/index.ts +110 -0
- 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
|
+
});
|