@mkaradeniz/eslint-config 5.1.0 → 5.3.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/base.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { fileURLToPath } from 'node:url';
2
2
  import eslintPluginImportX from 'eslint-plugin-import-x';
3
+ import eslintPluginJsdoc from 'eslint-plugin-jsdoc';
3
4
  import eslintPluginOnlyWarn from 'eslint-plugin-only-warn';
4
5
  import eslintPluginPerfectionist from 'eslint-plugin-perfectionist';
5
6
  import eslintPluginPreferArrowFunctions from 'eslint-plugin-prefer-arrow-functions';
@@ -83,6 +84,10 @@ export const baseConfig = [
83
84
  * https://github.com/un-ts/eslint-plugin-import-x
84
85
  */
85
86
  'import-x': eslintPluginImportX,
87
+ /**
88
+ * https://github.com/gajus/eslint-plugin-jsdoc
89
+ */
90
+ jsdoc: eslintPluginJsdoc,
86
91
  /**
87
92
  * https://github.com/bfanger/eslint-plugin-only-warn
88
93
  */
@@ -117,18 +122,6 @@ export const baseConfig = [
117
122
  '*.setup.ts',
118
123
  '**/*.stories.ts',
119
124
  '**/*.stories.tsx',
120
- '*/app/**/error.tsx',
121
- '*/app/**/global-error.tsx',
122
- '*/app/**/layout.tsx',
123
- '*/app/**/loading.tsx',
124
- '*/app/**/not-found.tsx',
125
- '*/app/**/opengraph-image.tsx',
126
- '*/app/**/page.tsx',
127
- '*/app/**/robots.ts',
128
- '*/app/**/route.tsx',
129
- '*/app/**/sitemap.ts',
130
- '*/app/**/template.tsx',
131
- '*/app/**/twitter-image.tsx',
132
125
  '*Config.mjs',
133
126
  '*Config.ts',
134
127
  '*config.js',
@@ -140,6 +133,15 @@ export const baseConfig = [
140
133
  'import-x/no-default-export': 'off',
141
134
  },
142
135
  },
136
+ {
137
+ files: ['**/env.ts', '**/env.client.ts', '**/env.server.ts', '**/seed.ts', '**/prisma.config.ts'],
138
+ rules: {
139
+ 'no-restricted-syntax': [
140
+ 'warn',
141
+ ...baseRules.rules['no-restricted-syntax'].slice(1).filter(selector => !selector.selector.includes('process')),
142
+ ],
143
+ },
144
+ },
143
145
  // Ignore patterns
144
146
  ...gitignoreConfig,
145
147
  { ignores: ['.next/**/*', 'dist/**/*', 'node_modules/**/*', 'out/**/*'] },
package/next.mjs CHANGED
@@ -11,4 +11,49 @@ export const nextConfig = [
11
11
  },
12
12
  },
13
13
  { ...nextRules },
14
+ {
15
+ files: [
16
+ '*/app/**/default.tsx',
17
+ '*/app/**/error.tsx',
18
+ '*/app/**/apple-icon.tsx',
19
+ '*/app/**/forbidden.tsx',
20
+ '*/app/**/global-error.tsx',
21
+ '*/app/**/icon.tsx',
22
+ '*/app/**/layout.tsx',
23
+ '*/app/**/loading.tsx',
24
+ '*/app/**/manifest.ts',
25
+ '*/app/**/not-found.tsx',
26
+ '*/app/**/opengraph-image.tsx',
27
+ '*/app/**/page.tsx',
28
+ '*/app/**/robots.ts',
29
+ '*/app/**/route.tsx',
30
+ '*/app/**/sitemap.ts',
31
+ '*/app/**/template.tsx',
32
+ '*/app/**/twitter-image.tsx',
33
+ '*/app/**/unauthorized.tsx',
34
+ '**/instrumentation.ts',
35
+ '**/instrumentation-client.ts',
36
+ '**/mdx-components.tsx',
37
+ '**/middleware.ts',
38
+ '**/proxy.ts',
39
+ ],
40
+ rules: {
41
+ 'import-x/no-default-export': ['off'],
42
+ },
43
+ },
44
+ {
45
+ files: [
46
+ '*/app/**/apple-icon.tsx',
47
+ '*/app/**/global-error.tsx',
48
+ '*/app/**/manifest.ts',
49
+ '*/app/**/not-found.tsx',
50
+ '*/app/**/opengraph-image.tsx',
51
+ '*/app/**/twitter-image.tsx',
52
+ '**/instrumentation-client.ts',
53
+ '**/mdx-components.tsx',
54
+ ],
55
+ rules: {
56
+ 'unicorn/filename-case': ['off'],
57
+ },
58
+ },
14
59
  ];
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@mkaradeniz/eslint-config",
3
- "version": "5.1.0",
3
+ "version": "5.3.0",
4
4
  "private": false,
5
5
  "files": [
6
6
  "base.mjs",
7
7
  "web.mjs",
8
8
  "next.mjs",
9
9
  "react.mjs",
10
+ "shadcn.mjs",
10
11
  "turbo.mjs",
11
12
  "eslint.config.mjs",
12
13
  "rules/"
@@ -28,6 +29,7 @@
28
29
  "@next/eslint-plugin-next": "^16.1.6",
29
30
  "@stylistic/eslint-plugin": "^5.8.0",
30
31
  "eslint-plugin-import-x": "^4.16.1",
32
+ "eslint-plugin-jsdoc": "^62.5.5",
31
33
  "eslint-plugin-jsx-a11y": "^6.10.2",
32
34
  "eslint-plugin-only-warn": "^1.1.0",
33
35
  "eslint-plugin-perfectionist": "^5.5.0",
@@ -98,6 +98,22 @@ export const baseRules = {
98
98
  message: 'Use !isNotNullOrUndefined() instead of === undefined.',
99
99
  selector: 'BinaryExpression[operator="==="][right.type="Identifier"][right.name="undefined"]',
100
100
  },
101
+ {
102
+ message: 'Exported functions with >1 parameter must use a single object parameter.',
103
+ selector: 'ExportNamedDeclaration > VariableDeclaration > VariableDeclarator > ArrowFunctionExpression[params.length>1]',
104
+ },
105
+ {
106
+ message: 'No IIFEs — create a utils file instead.',
107
+ selector: 'CallExpression[callee.type="ArrowFunctionExpression"]',
108
+ },
109
+ {
110
+ message: 'No IIFEs — create a utils file instead.',
111
+ selector: 'CallExpression[callee.type="FunctionExpression"]',
112
+ },
113
+ {
114
+ message: 'Use envConfigClient or envConfigServer instead of process.env.',
115
+ selector: 'MemberExpression[object.name="process"][property.name="env"]',
116
+ },
101
117
  ],
102
118
  'no-return-assign': ['warn'],
103
119
  'no-script-url': ['warn'],
@@ -161,6 +177,26 @@ export const baseRules = {
161
177
  'import-x/no-relative-packages': ['warn'],
162
178
  'import-x/no-self-import': ['warn'],
163
179
  'import-x/no-useless-path-segments': ['warn'],
180
+ // jsdoc
181
+ 'jsdoc/require-description': ['warn'],
182
+ 'jsdoc/require-jsdoc': [
183
+ 'warn',
184
+ {
185
+ contexts: [
186
+ 'ExportNamedDeclaration > VariableDeclaration > VariableDeclarator > ArrowFunctionExpression',
187
+ 'ExportNamedDeclaration > TSTypeAliasDeclaration > TSTypeLiteral > TSPropertySignature',
188
+ 'ExportNamedDeclaration > TSInterfaceDeclaration > TSInterfaceBody > TSPropertySignature',
189
+ ],
190
+ require: {
191
+ ArrowFunctionExpression: false,
192
+ ClassDeclaration: false,
193
+ ClassExpression: false,
194
+ FunctionDeclaration: false,
195
+ FunctionExpression: false,
196
+ MethodDefinition: false,
197
+ },
198
+ },
199
+ ],
164
200
  // perfectionist
165
201
  'perfectionist/sort-enums': ['warn', { ignoreCase: false, order: 'asc', partitionByNewLine: true, type: 'alphabetical' }],
166
202
  'perfectionist/sort-interfaces': ['warn', { ignoreCase: false, order: 'asc', partitionByNewLine: true, type: 'alphabetical' }],
@@ -36,6 +36,13 @@ export const reactRules = {
36
36
  name: 'react',
37
37
  },
38
38
  ],
39
+ patterns: [
40
+ {
41
+ allowImportNamePattern: 'Icon$',
42
+ group: ['lucide-react'],
43
+ message: 'Always use the Icon postfix when importing from lucide-react (e.g. CrownIcon, not Crown).',
44
+ },
45
+ ],
39
46
  },
40
47
  ],
41
48
  'no-restricted-syntax': [
@@ -55,6 +62,7 @@ export const reactRules = {
55
62
  'react/iframe-missing-sandbox': ['warn'],
56
63
  'react/jsx-boolean-value': ['warn', 'always'],
57
64
  'react/jsx-curly-brace-presence': ['warn', { children: 'ignore', props: 'never' }],
65
+ 'react/jsx-filename-extension': ['warn', { extensions: ['.tsx'] }],
58
66
  'react/jsx-fragments': ['warn'],
59
67
  'react/jsx-handler-names': ['warn', { checkLocalVariables: false, eventHandlerPrefix: 'handle', eventHandlerPropPrefix: 'on' }],
60
68
  'react/jsx-key': ['warn'],
@@ -105,6 +105,18 @@ export const typescriptRules = {
105
105
  '@typescript-eslint/prefer-optional-chain': ['warn'],
106
106
  '@typescript-eslint/prefer-readonly': ['warn'],
107
107
  '@typescript-eslint/require-await': ['warn'],
108
+ '@typescript-eslint/strict-boolean-expressions': [
109
+ 'warn',
110
+ {
111
+ allowNullableBoolean: false,
112
+ allowNullableEnum: false,
113
+ allowNullableNumber: false,
114
+ allowNullableObject: false,
115
+ allowNullableString: false,
116
+ allowNumber: false,
117
+ allowString: false,
118
+ },
119
+ ],
108
120
  '@typescript-eslint/switch-exhaustiveness-check': ['warn'],
109
121
  '@typescript-eslint/triple-slash-reference': ['warn'],
110
122
  '@typescript-eslint/unified-signatures': ['warn'],
package/shadcn.mjs ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Relaxes rules for shadcn/ui generated components.
3
+ *
4
+ * Factory function because the path to shadcn components varies per project.
5
+ * Only rules that conflict with how shadcn structurally organizes code are disabled —
6
+ * style rules (arrow functions, prop sorting, imports, etc.) stay enforced.
7
+ *
8
+ * @example
9
+ * import { createShadcnConfig } from '@mkaradeniz/eslint-config/shadcn.mjs';
10
+ * export default [...reactConfig, ...createShadcnConfig('src/components/ui')];
11
+ */
12
+ export const createShadcnConfig = (componentPath = '**/components/ui') => [
13
+ {
14
+ files: [`${componentPath}/**`],
15
+ rules: {
16
+ // shadcn prop types are wrappers around library types (Radix, Recharts) — self-documenting, JSDoc adds noise.
17
+ 'jsdoc/require-description': ['off'],
18
+ 'jsdoc/require-jsdoc': ['off'],
19
+ // shadcn ships multiple related components per file (e.g. Dialog, DialogTrigger, DialogContent).
20
+ 'react/no-multi-comp': ['off'],
21
+ // shadcn CLI expects exact filenames like `dialog.tsx`, `dropdown-menu.tsx`. Renaming breaks `npx shadcn add`.
22
+ 'unicorn/filename-case': ['off'],
23
+ },
24
+ },
25
+ ];