js-style-kit 0.8.1 → 0.8.3
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 +1 -1
- package/dist/bin/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +55 -43
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/eslint/convex/README.md +48 -0
- package/src/eslint/convex/config.ts +14 -1
- package/src/eslint/ignores.ts +1 -0
- package/src/eslint/index.ts +52 -50
- package/src/eslint/unicorn/README.md +17 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-style-kit",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "A zero configuration style guide for ESLint and Prettier",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -50,21 +50,21 @@
|
|
|
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
|
-
"eslint-plugin-jsdoc": "61.1.
|
|
53
|
+
"eslint-plugin-jsdoc": "61.1.8",
|
|
54
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
|
-
"eslint-plugin-react-hooks": "7.0.
|
|
58
|
+
"eslint-plugin-react-hooks": "7.0.1",
|
|
59
59
|
"eslint-plugin-react-refresh": "0.4.24",
|
|
60
|
-
"eslint-plugin-storybook": "9.1.
|
|
60
|
+
"eslint-plugin-storybook": "9.1.15",
|
|
61
61
|
"eslint-plugin-turbo": "2.5.8",
|
|
62
62
|
"eslint-plugin-unicorn": "61.0.2",
|
|
63
63
|
"eslint-plugin-vitest": "0.5.4",
|
|
64
64
|
"globals": "16.4.0",
|
|
65
65
|
"prettier": "3.6.2",
|
|
66
66
|
"prettier-plugin-css-order": "2.1.2",
|
|
67
|
-
"prettier-plugin-curly": "0.
|
|
67
|
+
"prettier-plugin-curly": "0.4.0",
|
|
68
68
|
"prettier-plugin-packagejson": "2.5.19",
|
|
69
69
|
"prettier-plugin-sort-json": "4.1.1",
|
|
70
70
|
"prettier-plugin-tailwindcss": "0.7.1",
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@repo/typescript-config": "workspace:*",
|
|
75
|
-
"@types/bun": "1.3.
|
|
76
|
-
"@types/node": "22.18.
|
|
77
|
-
"commander": "14.0.
|
|
75
|
+
"@types/bun": "1.3.1",
|
|
76
|
+
"@types/node": "22.18.12",
|
|
77
|
+
"commander": "14.0.2",
|
|
78
78
|
"glob": "11.0.3",
|
|
79
79
|
"tsup": "8.5.0",
|
|
80
80
|
"typescript": "5.9.3"
|
|
@@ -24,8 +24,56 @@ export default eslintConfig({
|
|
|
24
24
|
|
|
25
25
|
Convex rules apply only to files in your `convex` directory: `**/convex/**/*.{ts,js}`
|
|
26
26
|
|
|
27
|
+
## Filename Convention
|
|
28
|
+
|
|
29
|
+
When both Convex and Unicorn configurations are enabled, **Convex files are automatically enforced to use camelCase** naming, regardless of the global Unicorn filename case setting.
|
|
30
|
+
|
|
31
|
+
This is because Convex follows JavaScript/TypeScript conventions where files export functions that become API endpoints, and camelCase is the standard for function names.
|
|
32
|
+
|
|
33
|
+
### Example
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import { eslintConfig } from "js-style-kit";
|
|
37
|
+
|
|
38
|
+
export default eslintConfig({
|
|
39
|
+
convex: true,
|
|
40
|
+
unicorn: { filenameCase: "kebabCase" }, // Global setting
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
With this configuration:
|
|
45
|
+
|
|
46
|
+
- **Convex files** (`convex/**/*.{ts,js}`) must use **camelCase**: ✅ `getUserData.ts`, `sendMessage.ts`
|
|
47
|
+
- **All other files** must use **kebabCase**: ✅ `user-service.ts`, `api-client.ts`
|
|
48
|
+
|
|
49
|
+
### Why camelCase for Convex?
|
|
50
|
+
|
|
51
|
+
Convex files export functions that become your backend API. Using camelCase keeps your filenames consistent with the function names they export:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// convex/getUserData.ts
|
|
55
|
+
export default query(async (ctx) => {
|
|
56
|
+
// Function is called as api.getUserData
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Disabling Filename Enforcement
|
|
61
|
+
|
|
62
|
+
If you want to disable filename case enforcement for Convex files, you can override it:
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
export default eslintConfig({
|
|
66
|
+
convex: true,
|
|
67
|
+
unicorn: true,
|
|
68
|
+
rules: {
|
|
69
|
+
"unicorn/filename-case": "off", // Disables for all files including Convex
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
27
74
|
## Learn More
|
|
28
75
|
|
|
29
76
|
- [Convex ESLint Plugin](https://docs.convex.dev/eslint) - Official documentation
|
|
30
77
|
- [Convex Validators](https://docs.convex.dev/functions/validation) - Argument validation guide
|
|
78
|
+
- [Unicorn Configuration](../unicorn/README.md) - Filename case options
|
|
31
79
|
- [Main README](../../../README.md)
|
|
@@ -8,7 +8,7 @@ import { configNames } from "../constants.js";
|
|
|
8
8
|
import { convexRules } from "./rules.js";
|
|
9
9
|
|
|
10
10
|
// TODO: Replace with ESM import once @convex-dev/eslint-plugin stabilizes
|
|
11
|
-
// The
|
|
11
|
+
// The plugin doesn't have proper ESM exports yet
|
|
12
12
|
const require = createRequire(import.meta.url);
|
|
13
13
|
|
|
14
14
|
const convexPlugin = require("@convex-dev/eslint-plugin");
|
|
@@ -17,10 +17,12 @@ const convexPlugin = require("@convex-dev/eslint-plugin");
|
|
|
17
17
|
* Creates an ESLint configuration for Convex.
|
|
18
18
|
*
|
|
19
19
|
* @param customRules - Optional object containing custom rules to override or add to the Convex configuration.
|
|
20
|
+
* @param unicorn - Whether the config uses unicorn rules.
|
|
20
21
|
* @returns ESLint configuration object for Convex
|
|
21
22
|
*/
|
|
22
23
|
export const convexConfig = (
|
|
23
24
|
customRules?: Record<string, EslintRuleConfig>,
|
|
25
|
+
unicorn?: boolean,
|
|
24
26
|
): EslintConfigObject => ({
|
|
25
27
|
files: ["**/convex/**/*.{ts,js}"],
|
|
26
28
|
name: configNames.convex,
|
|
@@ -30,5 +32,16 @@ export const convexConfig = (
|
|
|
30
32
|
rules: {
|
|
31
33
|
...convexRules,
|
|
32
34
|
...(customRules ?? {}),
|
|
35
|
+
// Convex files must be camelCase
|
|
36
|
+
...(unicorn ?
|
|
37
|
+
{
|
|
38
|
+
"unicorn/filename-case": [
|
|
39
|
+
"warn",
|
|
40
|
+
{
|
|
41
|
+
case: "camelCase",
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
}
|
|
45
|
+
: {}),
|
|
33
46
|
},
|
|
34
47
|
});
|
package/src/eslint/ignores.ts
CHANGED
|
@@ -25,6 +25,7 @@ export const ignoresConfig = ({
|
|
|
25
25
|
}): Linter.Config => ({
|
|
26
26
|
ignores: [
|
|
27
27
|
"**/dist/",
|
|
28
|
+
"**/build/",
|
|
28
29
|
...(reactFramework === "next" ? [".next"] : []),
|
|
29
30
|
...(reactFramework === "react-router" ? [".react-router"] : []),
|
|
30
31
|
...(storybook ? ["!.storybook"] : []),
|
package/src/eslint/index.ts
CHANGED
|
@@ -64,7 +64,7 @@ export interface EslintConfigOptions {
|
|
|
64
64
|
* @param options - The optional configuration object.
|
|
65
65
|
* @param options.convex - Whether to include Convex rules.
|
|
66
66
|
* @param options.functionStyle - The function style to enforce. Defaults to "arrow".
|
|
67
|
-
* @param options.ignores - Additional paths to ignore. Already excludes `node_modules` and `
|
|
67
|
+
* @param options.ignores - Additional paths to ignore. Already excludes `node_modules`, `dist`, and `build`.
|
|
68
68
|
* @param options.importPlugin - Whether to include the import plugin. Defaults to true.
|
|
69
69
|
* @param options.jsdoc - Whether to include JSDoc rules. Set to false to disable, or provide an object to configure.
|
|
70
70
|
* @param options.query - Whether to include TanStack Query rules.
|
|
@@ -126,11 +126,10 @@ export const eslintConfig = (
|
|
|
126
126
|
),
|
|
127
127
|
];
|
|
128
128
|
|
|
129
|
-
if (
|
|
129
|
+
if (functionStyle === "arrow") {
|
|
130
130
|
configs.push(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
categorizedRules[configNames.jsdoc],
|
|
131
|
+
preferArrowFunctionConfig(
|
|
132
|
+
categorizedRules[configNames.preferArrowFunction],
|
|
134
133
|
),
|
|
135
134
|
);
|
|
136
135
|
}
|
|
@@ -150,44 +149,29 @@ export const eslintConfig = (
|
|
|
150
149
|
);
|
|
151
150
|
}
|
|
152
151
|
|
|
153
|
-
if (
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
// Apply reactRefresh based on framework setting or explicit override
|
|
157
|
-
const shouldUseReactRefresh =
|
|
158
|
-
// Explicit setting takes precedence
|
|
159
|
-
reactOptions.reactRefresh === true ||
|
|
160
|
-
// Framework-based default (vite/none use reactRefresh by default)
|
|
161
|
-
((reactOptions.framework === "vite" ||
|
|
162
|
-
reactOptions.framework === "none") &&
|
|
163
|
-
reactOptions.reactRefresh !== false);
|
|
164
|
-
|
|
165
|
-
if (shouldUseReactRefresh) {
|
|
166
|
-
configs.push(
|
|
167
|
-
reactRefreshEslintConfig(categorizedRules[configNames.reactRefresh]),
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
152
|
+
if (unicorn) {
|
|
153
|
+
const filenameCase = isObject(unicorn) ? unicorn.filenameCase : undefined;
|
|
171
154
|
configs.push(
|
|
172
|
-
|
|
173
|
-
customRules: categorizedRules[configNames.
|
|
174
|
-
|
|
175
|
-
reactCompiler: reactOptions.reactCompiler ?? true,
|
|
176
|
-
typescript: Boolean(typescript),
|
|
155
|
+
unicornConfig({
|
|
156
|
+
customRules: categorizedRules[configNames.unicorn],
|
|
157
|
+
filenameCase,
|
|
177
158
|
}),
|
|
178
159
|
);
|
|
179
|
-
|
|
180
|
-
if (isObject(react) && react.framework === "next") {
|
|
181
|
-
configs.push(nextjsConfig(categorizedRules[configNames.nextjs]));
|
|
182
|
-
}
|
|
183
160
|
}
|
|
184
161
|
|
|
185
|
-
if (
|
|
186
|
-
configs.push(
|
|
162
|
+
if (sorting) {
|
|
163
|
+
configs.push(
|
|
164
|
+
perfectionistConfig(categorizedRules[configNames.perfectionist]),
|
|
165
|
+
);
|
|
187
166
|
}
|
|
188
167
|
|
|
189
|
-
if (
|
|
190
|
-
configs.push(
|
|
168
|
+
if (jsdoc !== false) {
|
|
169
|
+
configs.push(
|
|
170
|
+
jsdocConfig(
|
|
171
|
+
jsdoc.requireJsdoc ?? false,
|
|
172
|
+
categorizedRules[configNames.jsdoc],
|
|
173
|
+
),
|
|
174
|
+
);
|
|
191
175
|
}
|
|
192
176
|
|
|
193
177
|
if (testing !== false) {
|
|
@@ -232,27 +216,45 @@ export const eslintConfig = (
|
|
|
232
216
|
);
|
|
233
217
|
}
|
|
234
218
|
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
219
|
+
if (react) {
|
|
220
|
+
const reactOptions = isObject(react) ? react : {};
|
|
221
|
+
|
|
222
|
+
// Apply reactRefresh based on framework setting or explicit override
|
|
223
|
+
const shouldUseReactRefresh =
|
|
224
|
+
// Explicit setting takes precedence
|
|
225
|
+
reactOptions.reactRefresh === true ||
|
|
226
|
+
// Framework-based default (vite/none use reactRefresh by default)
|
|
227
|
+
((reactOptions.framework === "vite" ||
|
|
228
|
+
reactOptions.framework === "none") &&
|
|
229
|
+
reactOptions.reactRefresh !== false);
|
|
230
|
+
|
|
231
|
+
if (shouldUseReactRefresh) {
|
|
232
|
+
configs.push(
|
|
233
|
+
reactRefreshEslintConfig(categorizedRules[configNames.reactRefresh]),
|
|
234
|
+
);
|
|
235
|
+
}
|
|
240
236
|
|
|
241
|
-
if (unicorn) {
|
|
242
|
-
const filenameCase = isObject(unicorn) ? unicorn.filenameCase : undefined;
|
|
243
237
|
configs.push(
|
|
244
|
-
|
|
245
|
-
customRules: categorizedRules[configNames.
|
|
246
|
-
|
|
238
|
+
reactEslintConfig({
|
|
239
|
+
customRules: categorizedRules[configNames.react],
|
|
240
|
+
functionStyle,
|
|
241
|
+
reactCompiler: reactOptions.reactCompiler ?? true,
|
|
242
|
+
typescript: Boolean(typescript),
|
|
247
243
|
}),
|
|
248
244
|
);
|
|
245
|
+
|
|
246
|
+
if (isObject(react) && react.framework === "next") {
|
|
247
|
+
configs.push(nextjsConfig(categorizedRules[configNames.nextjs]));
|
|
248
|
+
}
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
if (
|
|
251
|
+
if (query) {
|
|
252
|
+
configs.push(queryConfig(categorizedRules[configNames.query]));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (convex) {
|
|
252
256
|
configs.push(
|
|
253
|
-
|
|
254
|
-
categorizedRules[configNames.preferArrowFunction],
|
|
255
|
-
),
|
|
257
|
+
convexConfig(categorizedRules[configNames.convex], Boolean(unicorn)),
|
|
256
258
|
);
|
|
257
259
|
}
|
|
258
260
|
|
|
@@ -110,6 +110,23 @@ export default eslintConfig({
|
|
|
110
110
|
});
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
### Special Case: Convex Integration
|
|
114
|
+
|
|
115
|
+
When both Unicorn and Convex configurations are enabled, Convex files automatically use **camelCase** regardless of your global filename case setting. This is because Convex files export functions that become API endpoints, and camelCase is the standard for function names.
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
export default eslintConfig({
|
|
119
|
+
convex: true,
|
|
120
|
+
unicorn: { filenameCase: "kebabCase" }, // Global setting
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Result:
|
|
124
|
+
// - Convex files (convex/**/*.{ts,js}): camelCase ✅ getUserData.ts
|
|
125
|
+
// - All other files: kebabCase ✅ user-service.ts
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
[→ Learn more about Convex filename conventions](../convex/README.md#filename-convention)
|
|
129
|
+
|
|
113
130
|
### Node.js Protocol
|
|
114
131
|
|
|
115
132
|
Requires using the `node:` protocol when importing Node.js built-in modules:
|