eslint-config-axkit 1.1.0 → 1.2.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/dist/base-config.d.ts +2 -0
- package/dist/base-config.js +76 -0
- package/dist/import-optional.d.ts +9 -0
- package/dist/import-optional.js +30 -0
- package/dist/index.d.ts +17 -3
- package/dist/index.js +39 -164
- package/dist/vitest-config.d.ts +2 -0
- package/dist/vitest-config.js +62 -0
- package/package.json +26 -16
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import globals from "globals";
|
|
2
|
+
export const baseConfig = {
|
|
3
|
+
name: "axkit/base",
|
|
4
|
+
files: ["**/*.{js,mjs,cjs,ts,tsx,mts,cts}"],
|
|
5
|
+
languageOptions: {
|
|
6
|
+
globals: globals.node,
|
|
7
|
+
parserOptions: {
|
|
8
|
+
projectService: true,
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
rules: {
|
|
12
|
+
// Security rules
|
|
13
|
+
"no-eval": "error",
|
|
14
|
+
"no-new-func": "error",
|
|
15
|
+
"no-script-url": "error",
|
|
16
|
+
// Correctness rules
|
|
17
|
+
"no-return-assign": ["error", "always"],
|
|
18
|
+
radix: ["error", "as-needed"],
|
|
19
|
+
"guard-for-in": "error",
|
|
20
|
+
"prefer-object-has-own": "error",
|
|
21
|
+
// Clarity rules
|
|
22
|
+
"prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
|
|
23
|
+
"require-unicode-regexp": "error",
|
|
24
|
+
"no-extend-native": "error",
|
|
25
|
+
"no-new-wrappers": "error",
|
|
26
|
+
"no-implicit-coercion": ["error", { allow: ["!!"] }],
|
|
27
|
+
// Allow numbers in template literals (safe and common)
|
|
28
|
+
"@typescript-eslint/restrict-template-expressions": [
|
|
29
|
+
"error",
|
|
30
|
+
{ allowNumber: true },
|
|
31
|
+
],
|
|
32
|
+
// Disallow value-returning functions where void-returning expected
|
|
33
|
+
"@typescript-eslint/strict-void-return": "error",
|
|
34
|
+
// Disallow unused private class members (extends ESLint rule for TS private keyword)
|
|
35
|
+
"@typescript-eslint/no-unused-private-class-members": "error",
|
|
36
|
+
// ===== ESLint Core Bug Prevention =====
|
|
37
|
+
// Catch missing returns in array methods (.map, .filter, etc.)
|
|
38
|
+
"array-callback-return": ["error", { allowImplicit: false }],
|
|
39
|
+
// Constructors shouldn't return values
|
|
40
|
+
"no-constructor-return": "error",
|
|
41
|
+
// Returns in Promise executors are meaningless
|
|
42
|
+
"no-promise-executor-return": "error",
|
|
43
|
+
// Comparing variable to itself is always a bug
|
|
44
|
+
"no-self-compare": "error",
|
|
45
|
+
// Catch template literal syntax in regular strings
|
|
46
|
+
"no-template-curly-in-string": "error",
|
|
47
|
+
// Loops that only run once are bugs
|
|
48
|
+
"no-unreachable-loop": "error",
|
|
49
|
+
// Infinite loop detection (loop condition never changes)
|
|
50
|
+
"no-unmodified-loop-condition": "error",
|
|
51
|
+
// Dead code - assignments never read
|
|
52
|
+
"no-useless-assignment": "error",
|
|
53
|
+
// Race condition detection in async code
|
|
54
|
+
"require-atomic-updates": "error",
|
|
55
|
+
// Require === and !== (allow == null for null/undefined check)
|
|
56
|
+
eqeqeq: ["error", "always", { null: "ignore" }],
|
|
57
|
+
// Comma operator is confusing
|
|
58
|
+
"no-sequences": "error",
|
|
59
|
+
// ===== TypeScript-ESLint Enhancements =====
|
|
60
|
+
// Ensure switch statements handle all enum/union cases
|
|
61
|
+
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
|
62
|
+
// Use ?. instead of && chains (safer and cleaner)
|
|
63
|
+
"@typescript-eslint/prefer-optional-chain": "error",
|
|
64
|
+
// Require compare function for sort() to prevent string sorting bugs
|
|
65
|
+
"@typescript-eslint/require-array-sort-compare": "error",
|
|
66
|
+
// Prevent confusing ! placement next to == or !=
|
|
67
|
+
"@typescript-eslint/no-confusing-non-null-assertion": "error",
|
|
68
|
+
// Enforce import type for type-only imports
|
|
69
|
+
"@typescript-eslint/consistent-type-imports": [
|
|
70
|
+
"error",
|
|
71
|
+
{ prefer: "type-imports" },
|
|
72
|
+
],
|
|
73
|
+
// Enforce export type for type-only exports
|
|
74
|
+
"@typescript-eslint/consistent-type-exports": "error",
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamically import an optional dependency from the consumer's project.
|
|
3
|
+
*
|
|
4
|
+
* Uses `createRequire(cwd)` to resolve the module from the consumer's
|
|
5
|
+
* node_modules (not from this package), then `import()` the resolved path.
|
|
6
|
+
* This ensures transitive dependencies (e.g. `next` for `eslint-config-next`)
|
|
7
|
+
* are accessible.
|
|
8
|
+
*/
|
|
9
|
+
export declare function importOptional(name: string): Promise<unknown>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { pathToFileURL } from "node:url";
|
|
11
|
+
/**
|
|
12
|
+
* Dynamically import an optional dependency from the consumer's project.
|
|
13
|
+
*
|
|
14
|
+
* Uses `createRequire(cwd)` to resolve the module from the consumer's
|
|
15
|
+
* node_modules (not from this package), then `import()` the resolved path.
|
|
16
|
+
* This ensures transitive dependencies (e.g. `next` for `eslint-config-next`)
|
|
17
|
+
* are accessible.
|
|
18
|
+
*/
|
|
19
|
+
export async function importOptional(name) {
|
|
20
|
+
const require = createRequire(`${process.cwd()}/`);
|
|
21
|
+
let resolvedPath;
|
|
22
|
+
try {
|
|
23
|
+
resolvedPath = require.resolve(name);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new Error(`eslint-config-axkit: "${name}" must be installed to use this option. Run: pnpm add -D ${name}`);
|
|
27
|
+
}
|
|
28
|
+
const imported = (await import(__rewriteRelativeImportExtension(pathToFileURL(resolvedPath).href)));
|
|
29
|
+
return imported["default"] ?? imported;
|
|
30
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,20 @@ export type Options = {
|
|
|
5
5
|
* will be added to ESLint's ignore list.
|
|
6
6
|
*/
|
|
7
7
|
gitignorePath?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Enable Next.js rules (eslint-config-next core-web-vitals + typescript).
|
|
10
|
+
* Includes React, React Hooks, JSX accessibility, and Next.js-specific rules.
|
|
11
|
+
* Requires `eslint-config-next` to be installed.
|
|
12
|
+
*/
|
|
13
|
+
nextjs?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Enable Storybook rules (eslint-plugin-storybook flat/recommended).
|
|
16
|
+
* Requires `eslint-plugin-storybook` to be installed.
|
|
17
|
+
*/
|
|
18
|
+
storybook?: boolean;
|
|
8
19
|
};
|
|
9
20
|
/**
|
|
10
|
-
* Creates a complete ESLint flat config for TypeScript
|
|
21
|
+
* Creates a complete ESLint flat config for TypeScript projects.
|
|
11
22
|
*
|
|
12
23
|
* Includes:
|
|
13
24
|
* - ESLint recommended rules
|
|
@@ -15,6 +26,9 @@ export type Options = {
|
|
|
15
26
|
* - Unicorn plugin (recommended)
|
|
16
27
|
* - Vitest plugin for test files
|
|
17
28
|
* - Prettier compatibility (disables conflicting rules)
|
|
29
|
+
*
|
|
30
|
+
* Optional:
|
|
31
|
+
* - Next.js rules (core-web-vitals + typescript) via `nextjs: true`
|
|
32
|
+
* - Storybook rules (flat/recommended) via `storybook: true`
|
|
18
33
|
*/
|
|
19
|
-
export declare function axkit(options?: Options): Linter.Config[]
|
|
20
|
-
export default axkit;
|
|
34
|
+
export declare function axkit(options?: Options): Promise<Linter.Config[]>;
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import js from "@eslint/js";
|
|
2
|
-
import globals from "globals";
|
|
3
2
|
import tseslint from "typescript-eslint";
|
|
4
3
|
import { includeIgnoreFile } from "@eslint/compat";
|
|
5
4
|
import eslintConfigPrettier from "eslint-config-prettier/flat";
|
|
6
|
-
import vitest from "@vitest/eslint-plugin";
|
|
7
5
|
import eslintPluginUnicorn from "eslint-plugin-unicorn";
|
|
6
|
+
import { importOptional } from "./import-optional.js";
|
|
7
|
+
import { baseConfig } from "./base-config.js";
|
|
8
|
+
import { vitestConfig } from "./vitest-config.js";
|
|
8
9
|
/**
|
|
9
|
-
* Creates a complete ESLint flat config for TypeScript
|
|
10
|
+
* Creates a complete ESLint flat config for TypeScript projects.
|
|
10
11
|
*
|
|
11
12
|
* Includes:
|
|
12
13
|
* - ESLint recommended rules
|
|
@@ -14,183 +15,57 @@ import eslintPluginUnicorn from "eslint-plugin-unicorn";
|
|
|
14
15
|
* - Unicorn plugin (recommended)
|
|
15
16
|
* - Vitest plugin for test files
|
|
16
17
|
* - Prettier compatibility (disables conflicting rules)
|
|
18
|
+
*
|
|
19
|
+
* Optional:
|
|
20
|
+
* - Next.js rules (core-web-vitals + typescript) via `nextjs: true`
|
|
21
|
+
* - Storybook rules (flat/recommended) via `storybook: true`
|
|
17
22
|
*/
|
|
18
|
-
export function axkit(options = {}) {
|
|
19
|
-
const { gitignorePath } = options;
|
|
23
|
+
export async function axkit(options = {}) {
|
|
24
|
+
const { gitignorePath, nextjs, storybook } = options;
|
|
20
25
|
const configs = [];
|
|
26
|
+
// ── Gitignore ──────────────────────────────────────────────────────
|
|
21
27
|
if (gitignorePath) {
|
|
22
28
|
configs.push(includeIgnoreFile(gitignorePath, "Copy patterns from .gitignore"));
|
|
23
29
|
}
|
|
24
|
-
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"no-new-func": "error",
|
|
43
|
-
"no-script-url": "error",
|
|
44
|
-
// Correctness rules
|
|
45
|
-
"no-return-assign": ["error", "always"],
|
|
46
|
-
radix: ["error", "as-needed"],
|
|
47
|
-
"guard-for-in": "error",
|
|
48
|
-
"prefer-object-has-own": "error",
|
|
49
|
-
// Clarity rules
|
|
50
|
-
"prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
|
|
51
|
-
"require-unicode-regexp": "error",
|
|
52
|
-
"no-extend-native": "error",
|
|
53
|
-
"no-new-wrappers": "error",
|
|
54
|
-
"no-implicit-coercion": ["error", { allow: ["!!"] }],
|
|
55
|
-
// Allow numbers in template literals (safe and common)
|
|
56
|
-
"@typescript-eslint/restrict-template-expressions": [
|
|
57
|
-
"error",
|
|
58
|
-
{ allowNumber: true },
|
|
59
|
-
],
|
|
60
|
-
// Disallow value-returning functions where void-returning expected
|
|
61
|
-
"@typescript-eslint/strict-void-return": "error",
|
|
62
|
-
// Disallow unused private class members (extends ESLint rule for TS private keyword)
|
|
63
|
-
"@typescript-eslint/no-unused-private-class-members": "error",
|
|
64
|
-
// ===== ESLint Core Bug Prevention =====
|
|
65
|
-
// Catch missing returns in array methods (.map, .filter, etc.)
|
|
66
|
-
"array-callback-return": ["error", { allowImplicit: false }],
|
|
67
|
-
// Constructors shouldn't return values
|
|
68
|
-
"no-constructor-return": "error",
|
|
69
|
-
// Returns in Promise executors are meaningless
|
|
70
|
-
"no-promise-executor-return": "error",
|
|
71
|
-
// Comparing variable to itself is always a bug
|
|
72
|
-
"no-self-compare": "error",
|
|
73
|
-
// Catch template literal syntax in regular strings
|
|
74
|
-
"no-template-curly-in-string": "error",
|
|
75
|
-
// Loops that only run once are bugs
|
|
76
|
-
"no-unreachable-loop": "error",
|
|
77
|
-
// Infinite loop detection (loop condition never changes)
|
|
78
|
-
"no-unmodified-loop-condition": "error",
|
|
79
|
-
// Dead code - assignments never read
|
|
80
|
-
"no-useless-assignment": "error",
|
|
81
|
-
// Race condition detection in async code
|
|
82
|
-
"require-atomic-updates": "error",
|
|
83
|
-
// Require === and !== (allow == null for null/undefined check)
|
|
84
|
-
eqeqeq: ["error", "always", { null: "ignore" }],
|
|
85
|
-
// Comma operator is confusing
|
|
86
|
-
"no-sequences": "error",
|
|
87
|
-
// ===== TypeScript-ESLint Enhancements =====
|
|
88
|
-
// Ensure switch statements handle all enum/union cases
|
|
89
|
-
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
|
90
|
-
// Use ?. instead of && chains (safer and cleaner)
|
|
91
|
-
"@typescript-eslint/prefer-optional-chain": "error",
|
|
92
|
-
// Require compare function for sort() to prevent string sorting bugs
|
|
93
|
-
"@typescript-eslint/require-array-sort-compare": "error",
|
|
94
|
-
// Prevent confusing ! placement next to == or !=
|
|
95
|
-
"@typescript-eslint/no-confusing-non-null-assertion": "error",
|
|
96
|
-
// Enforce import type for type-only imports
|
|
97
|
-
"@typescript-eslint/consistent-type-imports": [
|
|
98
|
-
"error",
|
|
99
|
-
{ prefer: "type-imports" },
|
|
100
|
-
],
|
|
101
|
-
// Enforce export type for type-only exports
|
|
102
|
-
"@typescript-eslint/consistent-type-exports": "error",
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
// Unicorn plugin recommended config
|
|
106
|
-
eslintPluginUnicorn.configs.recommended,
|
|
107
|
-
// Enable unicorn rules that are off by default
|
|
30
|
+
// ── Next.js (before base so axkit's strict rules override) ─────────
|
|
31
|
+
//
|
|
32
|
+
// When nextjs is enabled, we must use the consumer's typescript-eslint
|
|
33
|
+
// instance (resolved from CWD) rather than our own bundled copy.
|
|
34
|
+
// eslint-config-next registers the @typescript-eslint plugin, and ESLint
|
|
35
|
+
// forbids redefining a plugin with a different object identity.
|
|
36
|
+
let tseslintModule = tseslint;
|
|
37
|
+
if (nextjs) {
|
|
38
|
+
tseslintModule = (await importOptional("typescript-eslint"));
|
|
39
|
+
const [nextVitals, nextTs] = await Promise.all([
|
|
40
|
+
importOptional("eslint-config-next/core-web-vitals"),
|
|
41
|
+
importOptional("eslint-config-next/typescript"),
|
|
42
|
+
]);
|
|
43
|
+
configs.push(...nextVitals, ...nextTs);
|
|
44
|
+
}
|
|
45
|
+
// ── Core configs ───────────────────────────────────────────────────
|
|
46
|
+
configs.push(js.configs.recommended, ...tseslintModule.configs.strictTypeChecked, baseConfig, eslintPluginUnicorn.configs.recommended,
|
|
47
|
+
// Unicorn rules that are off by default
|
|
108
48
|
{
|
|
109
49
|
name: "axkit/unicorn-extras",
|
|
110
50
|
files: ["**/*.{js,mjs,cjs,ts,tsx,mts,cts}"],
|
|
111
51
|
rules: {
|
|
112
|
-
// Prefer import.meta.dirname/filename over fileURLToPath (Node.js 20.11+)
|
|
113
52
|
"unicorn/prefer-import-meta-properties": "error",
|
|
114
|
-
// Optimize regex patterns (/[0-9]/ → /\d/)
|
|
115
53
|
"unicorn/better-regex": "error",
|
|
116
|
-
// Ensure correct Error subclassing (this.name, no redundant this.message)
|
|
117
54
|
"unicorn/custom-error-definition": "error",
|
|
118
|
-
// Catch unused object properties (dead code)
|
|
119
55
|
"unicorn/no-unused-properties": "error",
|
|
120
|
-
// Consistent numeric literal casing (0xFF, 2e5)
|
|
121
56
|
"unicorn/number-literal-case": "error",
|
|
122
57
|
},
|
|
123
|
-
},
|
|
124
|
-
// Disable type-checking for config files
|
|
125
|
-
{
|
|
58
|
+
}, {
|
|
126
59
|
name: "axkit/config-files",
|
|
127
60
|
files: ["*.config.{js,ts,mjs,mts}"],
|
|
128
|
-
...
|
|
129
|
-
},
|
|
130
|
-
//
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
plugins: { vitest },
|
|
138
|
-
rules: {
|
|
139
|
-
...vitest.configs.recommended.rules,
|
|
140
|
-
// Ensure expect.poll() and expect.element() are awaited
|
|
141
|
-
"vitest/require-awaited-expect-poll": "error",
|
|
142
|
-
// Keep vi.mock() and other hoisted APIs at the top of files
|
|
143
|
-
"vitest/hoisted-apis-on-top": "warn",
|
|
144
|
-
// Prefer toHaveBeenCalledTimes() matcher
|
|
145
|
-
"vitest/prefer-to-have-been-called-times": "warn",
|
|
146
|
-
// Prefer mockResolvedValue() over mockImplementation(() => Promise.resolve())
|
|
147
|
-
"vitest/prefer-mock-promise-shorthand": "warn",
|
|
148
|
-
// Prefer expectTypeOf() for type testing
|
|
149
|
-
"vitest/prefer-expect-type-of": "warn",
|
|
150
|
-
// Enforce consistent use of .each vs .for for parameterized tests
|
|
151
|
-
"vitest/consistent-each-for": "warn",
|
|
152
|
-
// Keep hooks (beforeEach, afterEach, etc.) at the top of describe blocks
|
|
153
|
-
"vitest/prefer-hooks-on-top": "warn",
|
|
154
|
-
// ===== Test Logic Errors =====
|
|
155
|
-
// No conditionals (if/else) inside test bodies - tests should be deterministic
|
|
156
|
-
"vitest/no-conditional-in-test": "error",
|
|
157
|
-
// No conditionally defined tests - tests should always run
|
|
158
|
-
"vitest/no-conditional-tests": "error",
|
|
159
|
-
// No return statements in tests - just execute assertions
|
|
160
|
-
"vitest/no-test-return-statement": "warn",
|
|
161
|
-
// No duplicate lifecycle hooks
|
|
162
|
-
"vitest/no-duplicate-hooks": "warn",
|
|
163
|
-
// ===== Better Matchers (auto-fixable) =====
|
|
164
|
-
// Use toBe() for primitives (strict equality)
|
|
165
|
-
"vitest/prefer-to-be": "warn",
|
|
166
|
-
// Use toHaveLength() instead of expect(arr.length).toBe()
|
|
167
|
-
"vitest/prefer-to-have-length": "warn",
|
|
168
|
-
// Use toContain() instead of expect(arr.includes()).toBe(true)
|
|
169
|
-
"vitest/prefer-to-contain": "warn",
|
|
170
|
-
// Use toBeGreaterThan(), toBeLessThan(), etc.
|
|
171
|
-
"vitest/prefer-comparison-matcher": "warn",
|
|
172
|
-
// Use dedicated equality matchers
|
|
173
|
-
"vitest/prefer-equality-matcher": "warn",
|
|
174
|
-
// ===== Best Practices =====
|
|
175
|
-
// Use vi.spyOn() instead of direct property assignment
|
|
176
|
-
"vitest/prefer-spy-on": "warn",
|
|
177
|
-
// Use test.todo() for empty/placeholder tests
|
|
178
|
-
"vitest/prefer-todo": "warn",
|
|
179
|
-
// Use expect().resolves instead of expect(await promise)
|
|
180
|
-
"vitest/prefer-expect-resolves": "warn",
|
|
181
|
-
// No deprecated alias methods (toBeCalled → toHaveBeenCalled)
|
|
182
|
-
"vitest/no-alias-methods": "warn",
|
|
183
|
-
// Use vi.mocked() instead of type casting
|
|
184
|
-
"vitest/prefer-vi-mocked": "warn",
|
|
185
|
-
// Use .only/.skip instead of f/x prefixes
|
|
186
|
-
"vitest/no-test-prefixes": "warn",
|
|
187
|
-
},
|
|
188
|
-
languageOptions: {
|
|
189
|
-
globals: { ...vitest.environments.env.globals },
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
// Prettier compatibility (must be last)
|
|
193
|
-
eslintConfigPrettier);
|
|
61
|
+
...tseslintModule.configs.disableTypeChecked,
|
|
62
|
+
}, vitestConfig);
|
|
63
|
+
// ── Storybook (after base, before Prettier) ────────────────────────
|
|
64
|
+
if (storybook) {
|
|
65
|
+
const storybookPlugin = (await importOptional("eslint-plugin-storybook"));
|
|
66
|
+
configs.push(...storybookPlugin.configs["flat/recommended"]);
|
|
67
|
+
}
|
|
68
|
+
// ── Prettier compatibility (must be last) ──────────────────────────
|
|
69
|
+
configs.push(eslintConfigPrettier);
|
|
194
70
|
return configs;
|
|
195
71
|
}
|
|
196
|
-
export default axkit;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import vitest from "@vitest/eslint-plugin";
|
|
2
|
+
export const vitestConfig = {
|
|
3
|
+
name: "axkit/vitest",
|
|
4
|
+
files: [
|
|
5
|
+
"**/*.{test,spec}.{ts,tsx,js,mjs,cjs,mts,cts}",
|
|
6
|
+
"tests/**/*.{ts,tsx,js,mjs,cjs,mts,cts}",
|
|
7
|
+
],
|
|
8
|
+
plugins: { vitest },
|
|
9
|
+
rules: {
|
|
10
|
+
...vitest.configs.recommended.rules,
|
|
11
|
+
// Ensure expect.poll() and expect.element() are awaited
|
|
12
|
+
"vitest/require-awaited-expect-poll": "error",
|
|
13
|
+
// Keep vi.mock() and other hoisted APIs at the top of files
|
|
14
|
+
"vitest/hoisted-apis-on-top": "warn",
|
|
15
|
+
// Prefer toHaveBeenCalledTimes() matcher
|
|
16
|
+
"vitest/prefer-to-have-been-called-times": "warn",
|
|
17
|
+
// Prefer mockResolvedValue() over mockImplementation(() => Promise.resolve())
|
|
18
|
+
"vitest/prefer-mock-promise-shorthand": "warn",
|
|
19
|
+
// Prefer expectTypeOf() for type testing
|
|
20
|
+
"vitest/prefer-expect-type-of": "warn",
|
|
21
|
+
// Enforce consistent use of .each vs .for for parameterized tests
|
|
22
|
+
"vitest/consistent-each-for": "warn",
|
|
23
|
+
// Keep hooks (beforeEach, afterEach, etc.) at the top of describe blocks
|
|
24
|
+
"vitest/prefer-hooks-on-top": "warn",
|
|
25
|
+
// ===== Test Logic Errors =====
|
|
26
|
+
// No conditionals (if/else) inside test bodies - tests should be deterministic
|
|
27
|
+
"vitest/no-conditional-in-test": "error",
|
|
28
|
+
// No conditionally defined tests - tests should always run
|
|
29
|
+
"vitest/no-conditional-tests": "error",
|
|
30
|
+
// No return statements in tests - just execute assertions
|
|
31
|
+
"vitest/no-test-return-statement": "warn",
|
|
32
|
+
// No duplicate lifecycle hooks
|
|
33
|
+
"vitest/no-duplicate-hooks": "warn",
|
|
34
|
+
// ===== Better Matchers (auto-fixable) =====
|
|
35
|
+
// Use toBe() for primitives (strict equality)
|
|
36
|
+
"vitest/prefer-to-be": "warn",
|
|
37
|
+
// Use toHaveLength() instead of expect(arr.length).toBe()
|
|
38
|
+
"vitest/prefer-to-have-length": "warn",
|
|
39
|
+
// Use toContain() instead of expect(arr.includes()).toBe(true)
|
|
40
|
+
"vitest/prefer-to-contain": "warn",
|
|
41
|
+
// Use toBeGreaterThan(), toBeLessThan(), etc.
|
|
42
|
+
"vitest/prefer-comparison-matcher": "warn",
|
|
43
|
+
// Use dedicated equality matchers
|
|
44
|
+
"vitest/prefer-equality-matcher": "warn",
|
|
45
|
+
// ===== Best Practices =====
|
|
46
|
+
// Use vi.spyOn() instead of direct property assignment
|
|
47
|
+
"vitest/prefer-spy-on": "warn",
|
|
48
|
+
// Use test.todo() for empty/placeholder tests
|
|
49
|
+
"vitest/prefer-todo": "warn",
|
|
50
|
+
// Use expect().resolves instead of expect(await promise)
|
|
51
|
+
"vitest/prefer-expect-resolves": "warn",
|
|
52
|
+
// No deprecated alias methods (toBeCalled → toHaveBeenCalled)
|
|
53
|
+
"vitest/no-alias-methods": "warn",
|
|
54
|
+
// Use vi.mocked() instead of type casting
|
|
55
|
+
"vitest/prefer-vi-mocked": "warn",
|
|
56
|
+
// Use .only/.skip instead of f/x prefixes
|
|
57
|
+
"vitest/no-test-prefixes": "warn",
|
|
58
|
+
},
|
|
59
|
+
languageOptions: {
|
|
60
|
+
globals: { ...vitest.environments.env.globals },
|
|
61
|
+
},
|
|
62
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "eslint-config-axkit",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.2.0",
|
|
6
6
|
"description": "Reusable ESLint flat config for TypeScript + Node.js projects with strict type checking, Prettier, Unicorn, and Vitest",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -53,33 +53,43 @@
|
|
|
53
53
|
"vitest",
|
|
54
54
|
"axkit"
|
|
55
55
|
],
|
|
56
|
-
"packageManager": "pnpm@10.
|
|
56
|
+
"packageManager": "pnpm@10.30.1",
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=22.14.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"eslint": ">=
|
|
61
|
+
"eslint": ">=10",
|
|
62
|
+
"eslint-config-next": ">=16",
|
|
63
|
+
"eslint-plugin-storybook": ">=10"
|
|
64
|
+
},
|
|
65
|
+
"peerDependenciesMeta": {
|
|
66
|
+
"eslint-config-next": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"eslint-plugin-storybook": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
62
72
|
},
|
|
63
73
|
"dependencies": {
|
|
64
|
-
"@eslint/compat": "^2.0.
|
|
65
|
-
"@eslint/js": "^
|
|
66
|
-
"@vitest/eslint-plugin": "^1.6.
|
|
74
|
+
"@eslint/compat": "^2.0.2",
|
|
75
|
+
"@eslint/js": "^10.0.1",
|
|
76
|
+
"@vitest/eslint-plugin": "^1.6.9",
|
|
67
77
|
"eslint-config-prettier": "^10.1.8",
|
|
68
|
-
"eslint-plugin-unicorn": "^
|
|
69
|
-
"globals": "^17.
|
|
70
|
-
"typescript-eslint": "^8.
|
|
78
|
+
"eslint-plugin-unicorn": "^63.0.0",
|
|
79
|
+
"globals": "^17.3.0",
|
|
80
|
+
"typescript-eslint": "^8.56.0"
|
|
71
81
|
},
|
|
72
82
|
"devDependencies": {
|
|
73
83
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
74
|
-
"@types/node": "^25.0
|
|
75
|
-
"@vitest/coverage-v8": "^4.0.
|
|
76
|
-
"eslint": "^
|
|
84
|
+
"@types/node": "^25.3.0",
|
|
85
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
86
|
+
"eslint": "^10.0.0",
|
|
77
87
|
"fta-check": "^1.5.1",
|
|
78
88
|
"fta-cli": "^3.0.0",
|
|
79
|
-
"knip": "^5.
|
|
80
|
-
"prettier": "3.
|
|
81
|
-
"semantic-release": "^25.0.
|
|
89
|
+
"knip": "^5.84.1",
|
|
90
|
+
"prettier": "3.8.1",
|
|
91
|
+
"semantic-release": "^25.0.3",
|
|
82
92
|
"typescript": "^5.9.3",
|
|
83
|
-
"vitest": "^4.0.
|
|
93
|
+
"vitest": "^4.0.18"
|
|
84
94
|
}
|
|
85
95
|
}
|