js-style-kit 0.7.0 → 0.8.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/bin/index.cjs +2 -1
- package/dist/bin/index.cjs.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.js +87 -29
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/eslint/ignores.ts +13 -10
- package/src/eslint/index.ts +32 -18
- package/src/eslint/testing/config.ts +21 -12
- package/src/eslint/testing/get-import-restrictions.ts +70 -0
- package/src/eslint/types.ts +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-style-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A zero configuration style guide for ESLint and Prettier",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -42,22 +42,22 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@convex-dev/eslint-plugin": "0.0
|
|
45
|
+
"@convex-dev/eslint-plugin": "1.0.0",
|
|
46
46
|
"@prettier/plugin-oxc": "0.0.4",
|
|
47
47
|
"@tanstack/eslint-plugin-query": "5.91.2",
|
|
48
48
|
"@typescript-eslint/parser": "8.46.0",
|
|
49
|
-
"eslint": "9.
|
|
49
|
+
"eslint": "9.38.0",
|
|
50
50
|
"eslint-import-resolver-typescript": "4.4.4",
|
|
51
51
|
"eslint-plugin-import-x": "4.16.1",
|
|
52
52
|
"eslint-plugin-jest": "29.0.1",
|
|
53
53
|
"eslint-plugin-jsdoc": "61.1.4",
|
|
54
|
-
"eslint-plugin-nextjs": "1.1.
|
|
54
|
+
"eslint-plugin-nextjs": "1.1.1",
|
|
55
55
|
"eslint-plugin-perfectionist": "4.15.1",
|
|
56
56
|
"eslint-plugin-prefer-arrow-functions": "3.9.1",
|
|
57
57
|
"eslint-plugin-react": "7.37.5",
|
|
58
58
|
"eslint-plugin-react-hooks": "7.0.0",
|
|
59
59
|
"eslint-plugin-react-refresh": "0.4.24",
|
|
60
|
-
"eslint-plugin-storybook": "9.1.
|
|
60
|
+
"eslint-plugin-storybook": "9.1.13",
|
|
61
61
|
"eslint-plugin-turbo": "2.5.8",
|
|
62
62
|
"eslint-plugin-unicorn": "61.0.2",
|
|
63
63
|
"eslint-plugin-vitest": "0.5.4",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"prettier-plugin-curly": "0.3.2",
|
|
68
68
|
"prettier-plugin-packagejson": "2.5.19",
|
|
69
69
|
"prettier-plugin-sort-json": "4.1.1",
|
|
70
|
-
"prettier-plugin-tailwindcss": "0.7.
|
|
70
|
+
"prettier-plugin-tailwindcss": "0.7.1",
|
|
71
71
|
"typescript-eslint": "8.46.0"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
package/src/eslint/ignores.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Linter } from "eslint";
|
|
2
2
|
|
|
3
|
+
import type { ReactFramework } from "./types.js";
|
|
4
|
+
|
|
3
5
|
import { configNames } from "./constants.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -7,23 +9,24 @@ import { configNames } from "./constants.js";
|
|
|
7
9
|
* By default, ignores node_modules, dist directories, and .git directories.
|
|
8
10
|
*
|
|
9
11
|
* @param options - Object with options to control the ignores configuration
|
|
10
|
-
* @param options.
|
|
11
|
-
* @param options.next - Whether to include .next directory in ignores
|
|
12
|
+
* @param options.reactFramework - The React framework being used
|
|
12
13
|
* @param options.storybook - Whether to include .storybook directory in ignores
|
|
14
|
+
* @param options.userIgnores - Additional glob patterns to ignore in ESLint checks
|
|
13
15
|
* @returns ESLint configuration object with ignore patterns
|
|
14
16
|
*/
|
|
15
17
|
export const ignoresConfig = ({
|
|
16
|
-
|
|
17
|
-
storybook
|
|
18
|
-
userIgnores
|
|
18
|
+
reactFramework,
|
|
19
|
+
storybook,
|
|
20
|
+
userIgnores,
|
|
19
21
|
}: {
|
|
20
|
-
|
|
21
|
-
storybook
|
|
22
|
-
userIgnores
|
|
23
|
-
}
|
|
22
|
+
reactFramework: ReactFramework;
|
|
23
|
+
storybook: boolean;
|
|
24
|
+
userIgnores: string[];
|
|
25
|
+
}): Linter.Config => ({
|
|
24
26
|
ignores: [
|
|
25
27
|
"**/dist/",
|
|
26
|
-
...(next ? [".next"] : []),
|
|
28
|
+
...(reactFramework === "next" ? [".next"] : []),
|
|
29
|
+
...(reactFramework === "react-router" ? [".react-router"] : []),
|
|
27
30
|
...(storybook ? ["!.storybook"] : []),
|
|
28
31
|
...userIgnores,
|
|
29
32
|
],
|
package/src/eslint/index.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Linter } from "eslint";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
EslintRuleConfig,
|
|
5
|
+
FilenameCase,
|
|
6
|
+
FunctionStyle,
|
|
7
|
+
ReactFramework,
|
|
8
|
+
} from "./types.js";
|
|
4
9
|
|
|
5
10
|
import { isObject, isString } from "../utils/is-type.js";
|
|
6
11
|
import { baseEslintConfig } from "./base/config.js";
|
|
@@ -22,14 +27,6 @@ import { turboConfig } from "./turbo/config.js";
|
|
|
22
27
|
import { tseslintConfig } from "./typescript/config.js";
|
|
23
28
|
import { unicornConfig } from "./unicorn/config.js";
|
|
24
29
|
|
|
25
|
-
const defaultTestingConfig: TestingConfig = {
|
|
26
|
-
filenamePattern: "test",
|
|
27
|
-
files: ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
|
|
28
|
-
formattingRules: true,
|
|
29
|
-
framework: "vitest",
|
|
30
|
-
itOrTest: "it",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
30
|
export interface EslintConfigOptions {
|
|
34
31
|
convex?: boolean;
|
|
35
32
|
functionStyle?: "off" | FunctionStyle;
|
|
@@ -44,7 +41,7 @@ export interface EslintConfigOptions {
|
|
|
44
41
|
react?:
|
|
45
42
|
| boolean
|
|
46
43
|
| {
|
|
47
|
-
framework?:
|
|
44
|
+
framework?: ReactFramework;
|
|
48
45
|
reactCompiler?: boolean;
|
|
49
46
|
reactRefresh?: boolean;
|
|
50
47
|
};
|
|
@@ -82,8 +79,9 @@ export interface EslintConfigOptions {
|
|
|
82
79
|
* @param options.testing - An object with the following properties:
|
|
83
80
|
* - `filenamePattern`: One of "spec" or "test" to determine which filename pattern to use.
|
|
84
81
|
* - `files`: Array of file patterns to include in the configuration.
|
|
85
|
-
* - `framework`: One of "vitest" or "jest" to determine which testing library to use.
|
|
82
|
+
* - `framework`: One of "vitest" or "jest" or "bun" or "node" to determine which testing library to use.
|
|
86
83
|
* - `formattingRules`: Whether to include formatting rules like padding around blocks.
|
|
84
|
+
* - `importRestrictions`: Whether to enforce imports from the correct testing framework.
|
|
87
85
|
* - `itOrTest`: One of "it" or "test" to determine which test function to use.
|
|
88
86
|
* @param options.typescript - Whether to include TypeScript rules. Can be a boolean or a string with path to tsconfig.
|
|
89
87
|
* @param options.turbo - Whether to include Turborepo rules. Defaults to false.
|
|
@@ -104,7 +102,7 @@ export const eslintConfig = (
|
|
|
104
102
|
rules,
|
|
105
103
|
sorting = true,
|
|
106
104
|
storybook = false,
|
|
107
|
-
testing
|
|
105
|
+
testing,
|
|
108
106
|
turbo = false,
|
|
109
107
|
typescript = true,
|
|
110
108
|
unicorn = { filenameCase: "kebabCase" },
|
|
@@ -114,11 +112,10 @@ export const eslintConfig = (
|
|
|
114
112
|
// Categorize user's custom rules first
|
|
115
113
|
const categorizedRules = rules === undefined ? {} : processCustomRules(rules);
|
|
116
114
|
|
|
117
|
-
const usingNextjs = isObject(react) && react.framework === "next";
|
|
118
|
-
|
|
119
115
|
const configs: Linter.Config[] = [
|
|
120
116
|
ignoresConfig({
|
|
121
|
-
|
|
117
|
+
reactFramework:
|
|
118
|
+
isObject(react) && react.framework ? react.framework : "none",
|
|
122
119
|
storybook,
|
|
123
120
|
userIgnores: ignores,
|
|
124
121
|
}),
|
|
@@ -180,7 +177,7 @@ export const eslintConfig = (
|
|
|
180
177
|
}),
|
|
181
178
|
);
|
|
182
179
|
|
|
183
|
-
if (
|
|
180
|
+
if (isObject(react) && react.framework === "next") {
|
|
184
181
|
configs.push(nextjsConfig(categorizedRules[configNames.nextjs]));
|
|
185
182
|
}
|
|
186
183
|
}
|
|
@@ -194,6 +191,15 @@ export const eslintConfig = (
|
|
|
194
191
|
}
|
|
195
192
|
|
|
196
193
|
if (testing !== false) {
|
|
194
|
+
const defaultTestingConfig: TestingConfig = {
|
|
195
|
+
filenamePattern: "test",
|
|
196
|
+
files: ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
|
|
197
|
+
formattingRules: true,
|
|
198
|
+
framework: "vitest",
|
|
199
|
+
importRestrictions: true,
|
|
200
|
+
itOrTest: "it",
|
|
201
|
+
};
|
|
202
|
+
|
|
197
203
|
// Use the provided testing config or the default if testing is true
|
|
198
204
|
const mergedTestingConfig: TestingConfig =
|
|
199
205
|
isObject(testing) ?
|
|
@@ -201,8 +207,14 @@ export const eslintConfig = (
|
|
|
201
207
|
: defaultTestingConfig;
|
|
202
208
|
|
|
203
209
|
// Destructure from the merged config
|
|
204
|
-
const {
|
|
205
|
-
|
|
210
|
+
const {
|
|
211
|
+
filenamePattern,
|
|
212
|
+
files,
|
|
213
|
+
formattingRules,
|
|
214
|
+
framework,
|
|
215
|
+
importRestrictions,
|
|
216
|
+
itOrTest,
|
|
217
|
+
} = mergedTestingConfig;
|
|
206
218
|
|
|
207
219
|
configs.push(
|
|
208
220
|
testingConfig(
|
|
@@ -211,7 +223,9 @@ export const eslintConfig = (
|
|
|
211
223
|
files,
|
|
212
224
|
formattingRules,
|
|
213
225
|
framework,
|
|
226
|
+
importRestrictions,
|
|
214
227
|
itOrTest,
|
|
228
|
+
typescript: Boolean(typescript),
|
|
215
229
|
},
|
|
216
230
|
categorizedRules[configNames.testing],
|
|
217
231
|
),
|
|
@@ -4,6 +4,7 @@ import vitest from "eslint-plugin-vitest";
|
|
|
4
4
|
import type { EslintConfigObject, EslintRuleConfig } from "../types.js";
|
|
5
5
|
|
|
6
6
|
import { configNames } from "../constants.js";
|
|
7
|
+
import { getImportRestrictions } from "./get-import-restrictions.js";
|
|
7
8
|
import { jestRules } from "./jest-rules.js";
|
|
8
9
|
import { vitestRules } from "./vitest-rules.js";
|
|
9
10
|
|
|
@@ -12,7 +13,15 @@ export interface TestingConfig {
|
|
|
12
13
|
files?: string[];
|
|
13
14
|
formattingRules?: boolean;
|
|
14
15
|
framework?: "bun" | "jest" | "node" | "vitest";
|
|
16
|
+
/**
|
|
17
|
+
* Whether to enforce imports from the correct testing framework.
|
|
18
|
+
* Uses the built-in ESLint `no-restricted-imports` rule.
|
|
19
|
+
*
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
importRestrictions?: boolean;
|
|
15
23
|
itOrTest?: "it" | "test";
|
|
24
|
+
typescript?: boolean;
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
/**
|
|
@@ -22,24 +31,23 @@ export interface TestingConfig {
|
|
|
22
31
|
* @param options.files - Files to include in the configuration
|
|
23
32
|
* @param options.filenamePattern - ".test" or ".spec" filename pattern
|
|
24
33
|
* @param options.itOrTest - "it" or "test"
|
|
25
|
-
* @param options.framework - "jest" or "vitest"
|
|
34
|
+
* @param options.framework - "jest" or "vitest" or "bun" or "node"
|
|
26
35
|
* @param options.formattingRules - Whether to include formatting rules like padding around blocks
|
|
36
|
+
* @param options.importRestrictions - Whether to enforce imports from the correct testing framework
|
|
37
|
+
* @param options.typescript - Whether the user is using TypeScript
|
|
27
38
|
* @param customRules - Optional object containing custom rules to override or add to the testing configuration.
|
|
28
39
|
* @returns ESLint configuration object
|
|
29
40
|
*/
|
|
30
41
|
export const testingConfig = (
|
|
31
42
|
{
|
|
32
|
-
filenamePattern,
|
|
43
|
+
filenamePattern = "test",
|
|
33
44
|
files,
|
|
34
|
-
formattingRules,
|
|
35
|
-
framework,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
framework: "vitest",
|
|
41
|
-
itOrTest: "test",
|
|
42
|
-
},
|
|
45
|
+
formattingRules = true,
|
|
46
|
+
framework = "vitest",
|
|
47
|
+
importRestrictions = true,
|
|
48
|
+
itOrTest = "test",
|
|
49
|
+
typescript = true,
|
|
50
|
+
}: TestingConfig = {},
|
|
43
51
|
customRules?: Record<string, EslintRuleConfig>,
|
|
44
52
|
): EslintConfigObject => ({
|
|
45
53
|
files: files ?? ["**/*.{test,spec}.{ts,tsx,js,jsx}"],
|
|
@@ -55,8 +63,8 @@ export const testingConfig = (
|
|
|
55
63
|
vitest,
|
|
56
64
|
},
|
|
57
65
|
rules: {
|
|
66
|
+
...(typescript ? { "@typescript-eslint/unbound-method": "off" } : {}),
|
|
58
67
|
// jest doesn't have a file name rule, so we'll use this one for both
|
|
59
|
-
"@typescript-eslint/unbound-method": "off",
|
|
60
68
|
"vitest/consistent-test-filename": [
|
|
61
69
|
"warn",
|
|
62
70
|
{
|
|
@@ -76,6 +84,7 @@ export const testingConfig = (
|
|
|
76
84
|
"jest/padding-around-test-blocks": "warn",
|
|
77
85
|
}
|
|
78
86
|
: {}),
|
|
87
|
+
...(importRestrictions ? getImportRestrictions(framework) : {}),
|
|
79
88
|
...(customRules ?? {}),
|
|
80
89
|
},
|
|
81
90
|
...(framework !== "jest" && framework !== "vitest" ?
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { EslintRuleConfig } from "../types.js";
|
|
2
|
+
|
|
3
|
+
const commonTestImports = [
|
|
4
|
+
"describe",
|
|
5
|
+
"it",
|
|
6
|
+
"test",
|
|
7
|
+
"expect",
|
|
8
|
+
"beforeAll",
|
|
9
|
+
"beforeEach",
|
|
10
|
+
"afterAll",
|
|
11
|
+
"afterEach",
|
|
12
|
+
"vi",
|
|
13
|
+
"mock",
|
|
14
|
+
"spyOn",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const frameworkConfig = {
|
|
18
|
+
bun: {
|
|
19
|
+
allowed: "'bun:test'",
|
|
20
|
+
restricted: ["vitest", "jest", "@jest/globals", "node:test"],
|
|
21
|
+
},
|
|
22
|
+
jest: {
|
|
23
|
+
allowed: "'jest' or '@jest/globals'",
|
|
24
|
+
restricted: ["vitest", "bun:test", "node:test"],
|
|
25
|
+
},
|
|
26
|
+
node: {
|
|
27
|
+
allowed: "'node:test'",
|
|
28
|
+
restricted: ["vitest", "jest", "@jest/globals", "bun:test"],
|
|
29
|
+
},
|
|
30
|
+
vitest: {
|
|
31
|
+
allowed: "'vitest'",
|
|
32
|
+
restricted: ["jest", "@jest/globals", "bun:test", "node:test"],
|
|
33
|
+
},
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generates the error message for restricted imports.
|
|
38
|
+
*
|
|
39
|
+
* @param allowedFramework - The allowed framework(s) for imports
|
|
40
|
+
* @returns The formatted error message
|
|
41
|
+
*/
|
|
42
|
+
const getRestrictionMessage = (allowedFramework: string): string =>
|
|
43
|
+
`This project is setup to use ${allowedFramework} for testing. Importing from other testing frameworks is not allowed. Change this setting in eslint.config.js under testing.framework`;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns import restriction rules based on the testing framework.
|
|
47
|
+
* Prevents importing from the wrong testing framework.
|
|
48
|
+
*
|
|
49
|
+
* @param framework - The testing framework being used
|
|
50
|
+
* @returns ESLint rules object with import restrictions
|
|
51
|
+
*/
|
|
52
|
+
export const getImportRestrictions = (
|
|
53
|
+
framework: "bun" | "jest" | "node" | "vitest",
|
|
54
|
+
): Record<string, EslintRuleConfig> => {
|
|
55
|
+
const config = frameworkConfig[framework];
|
|
56
|
+
const message = getRestrictionMessage(config.allowed);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
"no-restricted-imports": [
|
|
60
|
+
"warn",
|
|
61
|
+
{
|
|
62
|
+
paths: config.restricted.map((name) => ({
|
|
63
|
+
importNames: commonTestImports,
|
|
64
|
+
message,
|
|
65
|
+
name,
|
|
66
|
+
})),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
};
|
package/src/eslint/types.ts
CHANGED