js-style-kit 0.6.0 → 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 -77
  4. package/dist/index.js.map +1 -1
  5. package/package.json +36 -33
  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,259 @@
1
+ import type { Linter } from "eslint";
2
+
3
+ import type { EslintRuleConfig, FilenameCase, FunctionStyle } from "./types.js";
4
+
5
+ import { isObject, isString } from "../utils/is-type.js";
6
+ import { baseEslintConfig } from "./base/config.js";
7
+ import { configNames } from "./constants.js";
8
+ import { convexConfig } from "./convex/config.js";
9
+ import { ignoresConfig } from "./ignores.js";
10
+ import { importConfig } from "./import/config.js";
11
+ import { jsdocConfig } from "./jsdoc/config.js";
12
+ import { nextjsConfig } from "./nextjs/config.js";
13
+ import { perfectionistConfig } from "./perfectionist/config.js";
14
+ import { preferArrowFunctionConfig } from "./prefer-arrow-function/config.js";
15
+ import { processCustomRules } from "./process-custom-rules.js";
16
+ import { queryConfig } from "./query/config.js";
17
+ import { reactRefreshEslintConfig } from "./react-refresh/config.js";
18
+ import { reactEslintConfig } from "./react/config.js";
19
+ import { storybookConfig } from "./storybook/config.js";
20
+ import { testingConfig, type TestingConfig } from "./testing/config.js";
21
+ import { turboConfig } from "./turbo/config.js";
22
+ import { tseslintConfig } from "./typescript/config.js";
23
+ import { unicornConfig } from "./unicorn/config.js";
24
+
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
+ export interface EslintConfigOptions {
34
+ convex?: boolean;
35
+ functionStyle?: "off" | FunctionStyle;
36
+ ignores?: string[];
37
+ importPlugin?: boolean;
38
+ jsdoc?:
39
+ | false
40
+ | {
41
+ requireJsdoc?: boolean;
42
+ };
43
+ query?: boolean;
44
+ react?:
45
+ | boolean
46
+ | {
47
+ framework?: "next" | "none" | "react-router" | "remix" | "vite";
48
+ reactCompiler?: boolean;
49
+ reactRefresh?: boolean;
50
+ };
51
+ rules?: Record<string, EslintRuleConfig>;
52
+ sorting?: boolean;
53
+ storybook?: boolean;
54
+ testing?: false | TestingConfig;
55
+ turbo?: boolean;
56
+ typescript?: boolean | string;
57
+ unicorn?:
58
+ | boolean
59
+ | {
60
+ filenameCase?: FilenameCase;
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Configures ESLint based on provided options.
66
+ *
67
+ * @param options - The optional configuration object.
68
+ * @param options.convex - Whether to include Convex rules.
69
+ * @param options.functionStyle - The function style to enforce. Defaults to "arrow".
70
+ * @param options.ignores - Additional paths to ignore. Already excludes `node_modules` and `dist`.
71
+ * @param options.importPlugin - Whether to include the import plugin. Defaults to true.
72
+ * @param options.jsdoc - Whether to include JSDoc rules. Set to false to disable, or provide an object to configure.
73
+ * @param options.query - Whether to include TanStack Query rules.
74
+ * @param options.react - Whether to include React, React hooks, and React compiler rules.
75
+ * Can specify framework as "next", "none", "react-router", "remix", or "vite" to control related configs:
76
+ * - "next": Includes Next.js config, excludes React Refresh.
77
+ * - "vite" or "none": Includes React Refresh, excludes Next.js.
78
+ * - "remix" or "react-router": Excludes both Next.js and React Refresh (these frameworks handle their own refresh logic).
79
+ * - The reactRefresh property can override this framework-based behavior.
80
+ * @param options.sorting - Whether to include sorting rules from Perfectionist. Defaults to true.
81
+ * @param options.storybook - Whether to include Storybook rules. Defaults to false.
82
+ * @param options.testing - An object with the following properties:
83
+ * - `filenamePattern`: One of "spec" or "test" to determine which filename pattern to use.
84
+ * - `files`: Array of file patterns to include in the configuration.
85
+ * - `framework`: One of "vitest" or "jest" to determine which testing library to use.
86
+ * - `formattingRules`: Whether to include formatting rules like padding around blocks.
87
+ * - `itOrTest`: One of "it" or "test" to determine which test function to use.
88
+ * @param options.typescript - Whether to include TypeScript rules. Can be a boolean or a string with path to tsconfig.
89
+ * @param options.turbo - Whether to include Turborepo rules. Defaults to false.
90
+ * @param options.unicorn - Whether to include Unicorn rules. Defaults to true. Can be an object with filenameCase property.
91
+ * @param options.rules - This is for rules that you need to alter or turn off.
92
+ * @param additionalConfigs - Additional ESLint config objects to be merged into the final configuration.
93
+ * @returns An array of ESLint configuration objects.
94
+ */
95
+ export const eslintConfig = (
96
+ {
97
+ convex = false,
98
+ functionStyle = "arrow",
99
+ ignores = [],
100
+ importPlugin = true,
101
+ jsdoc = { requireJsdoc: false },
102
+ query = false,
103
+ react = false,
104
+ rules,
105
+ sorting = true,
106
+ storybook = false,
107
+ testing = defaultTestingConfig,
108
+ turbo = false,
109
+ typescript = true,
110
+ unicorn = { filenameCase: "kebabCase" },
111
+ }: EslintConfigOptions = {},
112
+ ...additionalConfigs: Linter.Config[]
113
+ ): Linter.Config[] => {
114
+ // Categorize user's custom rules first
115
+ const categorizedRules = rules === undefined ? {} : processCustomRules(rules);
116
+
117
+ const usingNextjs = isObject(react) && react.framework === "next";
118
+
119
+ const configs: Linter.Config[] = [
120
+ ignoresConfig({
121
+ next: usingNextjs,
122
+ storybook,
123
+ userIgnores: ignores,
124
+ }),
125
+ baseEslintConfig(
126
+ functionStyle,
127
+ Boolean(typescript),
128
+ categorizedRules[configNames.base],
129
+ ),
130
+ ];
131
+
132
+ if (jsdoc !== false) {
133
+ configs.push(
134
+ jsdocConfig(
135
+ jsdoc.requireJsdoc ?? false,
136
+ categorizedRules[configNames.jsdoc],
137
+ ),
138
+ );
139
+ }
140
+
141
+ if (typescript) {
142
+ configs.push(
143
+ ...(tseslintConfig(
144
+ isString(typescript) ? typescript : undefined,
145
+ categorizedRules[configNames.typescript],
146
+ ) as Linter.Config[]),
147
+ );
148
+ }
149
+
150
+ if (importPlugin) {
151
+ configs.push(
152
+ importConfig(Boolean(typescript), categorizedRules[configNames.import]),
153
+ );
154
+ }
155
+
156
+ if (react) {
157
+ const reactOptions = isObject(react) ? react : {};
158
+
159
+ // Apply reactRefresh based on framework setting or explicit override
160
+ const shouldUseReactRefresh =
161
+ // Explicit setting takes precedence
162
+ reactOptions.reactRefresh === true ||
163
+ // Framework-based default (vite/none use reactRefresh by default)
164
+ ((reactOptions.framework === "vite" ||
165
+ reactOptions.framework === "none") &&
166
+ reactOptions.reactRefresh !== false);
167
+
168
+ if (shouldUseReactRefresh) {
169
+ configs.push(
170
+ reactRefreshEslintConfig(categorizedRules[configNames.reactRefresh]),
171
+ );
172
+ }
173
+
174
+ configs.push(
175
+ reactEslintConfig({
176
+ customRules: categorizedRules[configNames.react],
177
+ functionStyle,
178
+ reactCompiler: reactOptions.reactCompiler ?? true,
179
+ typescript: Boolean(typescript),
180
+ }),
181
+ );
182
+
183
+ if (usingNextjs) {
184
+ configs.push(nextjsConfig(categorizedRules[configNames.nextjs]));
185
+ }
186
+ }
187
+
188
+ if (query) {
189
+ configs.push(queryConfig(categorizedRules[configNames.query]));
190
+ }
191
+
192
+ if (convex) {
193
+ configs.push(convexConfig(categorizedRules[configNames.convex]));
194
+ }
195
+
196
+ if (testing !== false) {
197
+ // Use the provided testing config or the default if testing is true
198
+ const mergedTestingConfig: TestingConfig =
199
+ isObject(testing) ?
200
+ { ...defaultTestingConfig, ...testing }
201
+ : defaultTestingConfig;
202
+
203
+ // Destructure from the merged config
204
+ const { filenamePattern, files, formattingRules, framework, itOrTest } =
205
+ mergedTestingConfig;
206
+
207
+ configs.push(
208
+ testingConfig(
209
+ {
210
+ filenamePattern,
211
+ files,
212
+ formattingRules,
213
+ framework,
214
+ itOrTest,
215
+ },
216
+ categorizedRules[configNames.testing],
217
+ ),
218
+ );
219
+ }
220
+
221
+ if (sorting) {
222
+ configs.push(
223
+ perfectionistConfig(categorizedRules[configNames.perfectionist]),
224
+ );
225
+ }
226
+
227
+ if (unicorn) {
228
+ const filenameCase = isObject(unicorn) ? unicorn.filenameCase : undefined;
229
+ configs.push(
230
+ unicornConfig({
231
+ customRules: categorizedRules[configNames.unicorn],
232
+ filenameCase,
233
+ }),
234
+ );
235
+ }
236
+
237
+ if (functionStyle === "arrow") {
238
+ configs.push(
239
+ preferArrowFunctionConfig(
240
+ categorizedRules[configNames.preferArrowFunction],
241
+ ),
242
+ );
243
+ }
244
+
245
+ if (storybook) {
246
+ configs.push(...storybookConfig(categorizedRules[configNames.storybook]));
247
+ }
248
+
249
+ if (turbo) {
250
+ configs.push(turboConfig(categorizedRules[configNames.turbo]));
251
+ }
252
+
253
+ // Add any additional config objects provided by the user
254
+ if (additionalConfigs.length > 0) {
255
+ configs.push(...additionalConfigs);
256
+ }
257
+
258
+ return configs;
259
+ };
@@ -0,0 +1,399 @@
1
+ # JSDoc Configuration
2
+
3
+ JSDoc comment validation and formatting for better code documentation.
4
+
5
+ [← Back to main README](../../../README.md)
6
+
7
+ ## Overview
8
+
9
+ JSDoc configuration is **disabled by default** and provides:
10
+
11
+ - JSDoc comment validation
12
+ - Type and tag checking
13
+ - Parameter and return value documentation
14
+ - Formatting and style consistency
15
+ - TypeScript-aware (disables redundant type rules)
16
+
17
+ This plugin can be handy for libraries, but it can get annoying, so it's not for everyone. [See my configuration](./rules.ts), and feel free to customize via the `rules` option.
18
+
19
+ ## Quick Start
20
+
21
+ ```js
22
+ import { eslintConfig } from "js-style-kit";
23
+
24
+ export default eslintConfig({
25
+ jsdoc: true, // Enable JSDoc validation
26
+ });
27
+ ```
28
+
29
+ ## Configuration Options
30
+
31
+ ### Basic Enable
32
+
33
+ ```js
34
+ jsdoc: true, // Validation without requiring JSDoc on all functions
35
+ ```
36
+
37
+ ### Require JSDoc
38
+
39
+ ```js
40
+ jsdoc: {
41
+ requireJsdoc: true, // Enforce JSDoc on functions and classes
42
+ }
43
+ ```
44
+
45
+ ## File Scope
46
+
47
+ JSDoc rules apply to:
48
+
49
+ - ✅ All `.js`, `.jsx`, `.ts`, `.tsx`, `.cjs`, `.mjs` files
50
+ - ❌ Excluded from `.test` and `.spec` files
51
+
52
+ ## Validation Without Enforcement
53
+
54
+ By default (`requireJsdoc: false`), JSDoc comments are **validated but not required**:
55
+
56
+ ```ts
57
+ // ✅ Good - no JSDoc required, but valid if present
58
+ export const add = (a: number, b: number) => a + b;
59
+
60
+ // ✅ Good - JSDoc is validated when present
61
+ /**
62
+ * Adds two numbers together.
63
+ *
64
+ * @param a - First number
65
+ * @param b - Second number
66
+ * @returns Sum of the two numbers
67
+ */
68
+ export const add = (a: number, b: number) => a + b;
69
+
70
+ // ❌ Bad - invalid JSDoc syntax
71
+ /**
72
+ * @param c - Wrong parameter name
73
+ */
74
+ export const add = (a: number, b: number) => a + b;
75
+ ```
76
+
77
+ **Active rules:**
78
+
79
+ - ✅ Validates JSDoc syntax and formatting
80
+ - ✅ Checks parameter names match function signature
81
+ - ✅ Verifies tag names are valid
82
+ - ❌ Does NOT require JSDoc on all functions
83
+
84
+ ## Require JSDoc Mode
85
+
86
+ When `requireJsdoc: true`, documentation becomes mandatory:
87
+
88
+ ```ts
89
+ // ❌ Bad - missing JSDoc
90
+ export const add = (a: number, b: number) => a + b;
91
+
92
+ // ✅ Good - complete JSDoc
93
+ /**
94
+ * Adds two numbers together.
95
+ *
96
+ * @param a - First number
97
+ * @param b - Second number
98
+ * @returns Sum of the two numbers
99
+ */
100
+ export const add = (a: number, b: number) => a + b;
101
+ ```
102
+
103
+ **Requires JSDoc on:**
104
+
105
+ - Function declarations
106
+ - Function expressions
107
+ - Arrow functions
108
+ - Class declarations
109
+ - Class expressions
110
+ - Method definitions
111
+
112
+ ## TypeScript Integration
113
+
114
+ When using TypeScript, JSDoc rules automatically adjust:
115
+
116
+ ```ts
117
+ // ✅ Good - TypeScript handles types
118
+ /**
119
+ * Calculates the total price with tax.
120
+ *
121
+ * @param price - Base price
122
+ * @param taxRate - Tax rate as decimal
123
+ * @returns Total price including tax
124
+ */
125
+ export const calculateTotal = (price: number, taxRate: number): number => {
126
+ return price * (1 + taxRate);
127
+ };
128
+
129
+ // ❌ Bad - redundant type in JSDoc (TypeScript projects)
130
+ /**
131
+ * @param {number} price - Base price
132
+ * @param {number} taxRate - Tax rate
133
+ * @returns {number} Total
134
+ */
135
+ export const calculateTotal = (price: number, taxRate: number): number => {
136
+ return price * (1 + taxRate);
137
+ };
138
+ ```
139
+
140
+ **TypeScript-specific behavior:**
141
+
142
+ - `jsdoc/no-types` is enabled (prevents redundant `{type}` annotations)
143
+ - `jsdoc/no-undefined-types` is disabled (TypeScript validates types)
144
+ - Type information comes from TypeScript, not JSDoc
145
+
146
+ ## Key Rules
147
+
148
+ ### Syntax Validation
149
+
150
+ - **`jsdoc/check-alignment`** - Enforces proper JSDoc alignment
151
+ - **`jsdoc/check-tag-names`** - Validates tag names are recognized
152
+ - **`jsdoc/check-types`** - Validates JSDoc types (non-TypeScript)
153
+ - **`jsdoc/valid-types`** - Ensures type syntax is valid
154
+ - **`jsdoc/empty-tags`** - Validates tags that shouldn't have content
155
+
156
+ ### Parameter Documentation
157
+
158
+ - **`jsdoc/check-param-names`** - Parameter names match function signature
159
+ - **`jsdoc/require-param`** - Requires `@param` tags (when `requireJsdoc: true`)
160
+ - **`jsdoc/require-param-name`** - Ensures `@param` has parameter name
161
+ - **`jsdoc/require-param-description`** - Requires parameter descriptions
162
+
163
+ ### Return Documentation
164
+
165
+ - **`jsdoc/require-returns`** - Requires `@returns` tag (when `requireJsdoc: true`)
166
+ - **`jsdoc/require-returns-check`** - Validates `@returns` matches return statement
167
+ - **`jsdoc/require-returns-description`** - Requires return value description
168
+
169
+ ### Formatting
170
+
171
+ - **`jsdoc/multiline-blocks`** - Enforces multiline JSDoc format
172
+ - **`jsdoc/no-multi-asterisks`** - Prevents extra asterisks
173
+ - **`jsdoc/require-asterisk-prefix`** - Requires asterisk on each line
174
+ - **`jsdoc/tag-lines`** - Controls spacing between tags
175
+
176
+ ### Other Rules
177
+
178
+ - **`jsdoc/check-access`** - Validates `@private`, `@public`, etc.
179
+ - **`jsdoc/check-property-names`** - Validates `@property` tag names
180
+ - **`jsdoc/implements-on-classes`** - Ensures `@implements` on classes only
181
+ - **`jsdoc/no-defaults`** - Prevents documenting default values
182
+
183
+ ## Examples
184
+
185
+ ### Function Documentation
186
+
187
+ ```ts
188
+ /**
189
+ * Fetches user data from the API.
190
+ *
191
+ * @param userId - The unique identifier for the user
192
+ * @returns Promise resolving to user data
193
+ * @throws {Error} When the user is not found
194
+ */
195
+ export const fetchUser = async (userId: string): Promise<User> => {
196
+ const response = await fetch(`/api/users/${userId}`);
197
+ if (!response.ok) throw new Error("User not found");
198
+ return response.json();
199
+ };
200
+ ```
201
+
202
+ ### Class Documentation
203
+
204
+ ```ts
205
+ /**
206
+ * Manages user authentication and sessions.
207
+ */
208
+ export class AuthService {
209
+ /**
210
+ * Authenticates a user with credentials.
211
+ *
212
+ * @param username - User's username
213
+ * @param password - User's password
214
+ * @returns Authentication token
215
+ */
216
+ login(username: string, password: string): string {
217
+ // Implementation
218
+ }
219
+
220
+ /**
221
+ * Logs out the current user.
222
+ */
223
+ logout(): void {
224
+ // Implementation
225
+ }
226
+ }
227
+ ```
228
+
229
+ ### Interface Documentation
230
+
231
+ ```ts
232
+ /**
233
+ * Represents a user in the system.
234
+ */
235
+ interface User {
236
+ /** Unique user identifier */
237
+ id: string;
238
+
239
+ /** User's display name */
240
+ name: string;
241
+
242
+ /** User's email address */
243
+ email: string;
244
+ }
245
+ ```
246
+
247
+ ### Destructured Parameters
248
+
249
+ ```ts
250
+ /**
251
+ * Creates a new user account.
252
+ *
253
+ * @param options - User creation options
254
+ * @param options.name - User's name
255
+ * @param options.email - User's email
256
+ * @param options.role - User's role in the system
257
+ * @returns Created user object
258
+ */
259
+ export const createUser = ({
260
+ name,
261
+ email,
262
+ role,
263
+ }: {
264
+ name: string;
265
+ email: string;
266
+ role: string;
267
+ }): User => {
268
+ // Implementation
269
+ };
270
+ ```
271
+
272
+ ### Async Functions
273
+
274
+ ```ts
275
+ /**
276
+ * Processes payment for an order.
277
+ *
278
+ * @param orderId - Order identifier
279
+ * @param amount - Payment amount
280
+ * @returns Promise resolving to payment confirmation
281
+ * @throws {PaymentError} When payment processing fails
282
+ */
283
+ export const processPayment = async (
284
+ orderId: string,
285
+ amount: number,
286
+ ): Promise<PaymentConfirmation> => {
287
+ // Implementation
288
+ };
289
+ ```
290
+
291
+ ## Tag Formatting
292
+
293
+ JSDoc enforces consistent tag spacing:
294
+
295
+ ```ts
296
+ // ✅ Good - proper tag formatting
297
+ /**
298
+ * Description here.
299
+ *
300
+ * @param name - Parameter description
301
+ * @param age - Parameter description
302
+ * @returns Return description
303
+ */
304
+
305
+ // ❌ Bad - extra lines between tags
306
+ /**
307
+ * Description here.
308
+ *
309
+ * @param name - Parameter description
310
+ *
311
+ * @param age - Parameter description
312
+ *
313
+ * @returns Return description
314
+ */
315
+ ```
316
+
317
+ **Rules:**
318
+
319
+ - One blank line between description and first tag
320
+ - No blank lines between `@param` tags
321
+ - Consistent formatting throughout
322
+
323
+ ## Customization
324
+
325
+ ### Require JSDoc on Exports Only
326
+
327
+ ```js
328
+ export default eslintConfig({
329
+ jsdoc: { requireJsdoc: true },
330
+ rules: {
331
+ "jsdoc/require-jsdoc": [
332
+ "warn",
333
+ {
334
+ require: {
335
+ FunctionDeclaration: true,
336
+ // Only require JSDoc on exported functions
337
+ publicOnly: true,
338
+ },
339
+ },
340
+ ],
341
+ },
342
+ });
343
+ ```
344
+
345
+ ### Allow JSDoc Types in TypeScript
346
+
347
+ ```js
348
+ export default eslintConfig({
349
+ jsdoc: true,
350
+ rules: {
351
+ // Allow {type} annotations even in TypeScript
352
+ "jsdoc/no-types": "off",
353
+ },
354
+ });
355
+ ```
356
+
357
+ ### Disable Description Requirements
358
+
359
+ ```js
360
+ export default eslintConfig({
361
+ jsdoc: true,
362
+ rules: {
363
+ "jsdoc/require-param-description": "off",
364
+ "jsdoc/require-returns-description": "off",
365
+ },
366
+ });
367
+ ```
368
+
369
+ ## Common Patterns
370
+
371
+ ### Library Development
372
+
373
+ ```js
374
+ export default eslintConfig({
375
+ typescript: true,
376
+ jsdoc: { requireJsdoc: true }, // Require docs for public API
377
+ });
378
+ ```
379
+
380
+ ### Application Development
381
+
382
+ ```js
383
+ export default eslintConfig({
384
+ typescript: true,
385
+ jsdoc: true, // Validate JSDoc but don't require it
386
+ });
387
+ ```
388
+
389
+ ## Related Configurations
390
+
391
+ - [TypeScript](../typescript/README.md) - TypeScript type checking
392
+ - [Base](../base/README.md) - Base ESLint rules
393
+
394
+ ## Learn More
395
+
396
+ - [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc)
397
+ - [JSDoc Official](https://jsdoc.app/)
398
+ - [TypeScript JSDoc Reference](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html)
399
+ - [Main README](../../../README.md)
@@ -0,0 +1,29 @@
1
+ import jsdoc from "eslint-plugin-jsdoc";
2
+
3
+ import type { EslintConfigObject, EslintRuleConfig } from "../types.js";
4
+
5
+ import { configNames } from "../constants.js";
6
+ import { jsdocRules } from "./rules.js";
7
+
8
+ /**
9
+ * Generates ESLint configuration for JSDoc comments.
10
+ *
11
+ * @param requireJsdoc - Whether to enforce JSDoc comments on functions and classes. Defaults to false.
12
+ * @param customRules - Optional object containing custom rules to override or add to the JSDoc configuration.
13
+ * @returns An ESLint configuration object for JSDoc comments.
14
+ */
15
+ export const jsdocConfig = (
16
+ requireJsdoc = false,
17
+ customRules?: Record<string, EslintRuleConfig>,
18
+ ): EslintConfigObject => ({
19
+ files: ["**/*.{js,jsx,ts,tsx,cjs,mjs}"],
20
+ ignores: ["**/*.{test,spec}.{js,jsx,ts,tsx,cjs,mjs}"],
21
+ name: configNames.jsdoc,
22
+ plugins: {
23
+ jsdoc,
24
+ },
25
+ rules: {
26
+ ...jsdocRules(requireJsdoc),
27
+ ...(customRules ?? {}),
28
+ },
29
+ });