@miskamyasa/eslint-config 1.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Denis Zaitsev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @miskamyasa/eslint-config
2
+
3
+ Opinionated ESLint config for React + TypeScript projects.
4
+
5
+ ## What's included
6
+
7
+ - ESLint recommended rules
8
+ - TypeScript strict type checking (`typescript-eslint` recommended + strictTypeChecked + stylisticTypeChecked)
9
+ - React rules (`@eslint-react/eslint-plugin` recommended-typescript + `react-hooks`)
10
+ - Import ordering and validation (`eslint-plugin-import-x` + `eslint-plugin-unused-imports`)
11
+ - Stylistic formatting (`@stylistic/eslint-plugin` with double quotes, no semicolons, 2-space indent, stroustrup brace style)
12
+ - CommonJS overrides for `.js`/`.cjs` files
13
+
14
+ ## Requirements
15
+
16
+ - Node.js >= 22
17
+ - ESLint >= 9
18
+ - TypeScript >= 5
19
+
20
+ ## Install
21
+
22
+ ```sh
23
+ pnpm add -D @miskamyasa/eslint-config eslint typescript
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Create `eslint.config.ts` in your project root:
29
+
30
+ ```ts
31
+ import createConfig from "@miskamyasa/eslint-config"
32
+
33
+ export default createConfig({
34
+ tsconfigRootDir: import.meta.dirname,
35
+ })
36
+ ```
37
+
38
+ > ESLint 10 requires `jiti` for TypeScript config files: `pnpm add -D jiti`
39
+
40
+ ## Options
41
+
42
+ | Option | Type | Default | Description |
43
+ |---|---|---|---|
44
+ | `tsconfigRootDir` | `string` | `process.cwd()` | Root directory for TypeScript project service |
45
+ | `ignores` | `string[]` | `[]` | Additional glob patterns to ignore |
46
+
47
+ ## Overrides
48
+
49
+ Pass additional ESLint config objects after the options to override or extend rules:
50
+
51
+ ```ts
52
+ import createConfig from "@miskamyasa/eslint-config"
53
+
54
+ export default createConfig(
55
+ {tsconfigRootDir: import.meta.dirname},
56
+ {
57
+ rules: {
58
+ "no-console": "off",
59
+ },
60
+ },
61
+ )
62
+ ```
63
+
64
+ ## Named exports
65
+
66
+ Individual config functions are available for advanced composition:
67
+
68
+ ```ts
69
+ import {typescript, stylistic, imports, react, javascript} from "@miskamyasa/eslint-config"
70
+ ```
71
+
72
+ ## Key style decisions
73
+
74
+ - Double quotes
75
+ - No semicolons
76
+ - 2-space indentation
77
+ - Stroustrup brace style
78
+ - No curly spacing (`{a}` not `{ a }`)
79
+ - Max line length: 120 (warning)
80
+ - Unix line endings
81
+ - Default exports disallowed (`import-x/no-default-export`)
82
+ - Imports sorted alphabetically with React first
83
+ - Unused imports auto-removed
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,47 @@
1
+ import { Linter } from 'eslint';
2
+
3
+ declare function createImportsConfig(): Linter.Config[];
4
+
5
+ declare function javascript(): Linter.Config[];
6
+
7
+ declare function react(): Linter.Config[];
8
+
9
+ declare function stylistic(): Linter.Config[];
10
+
11
+ declare function typescript(options: {
12
+ tsconfigRootDir: string;
13
+ }): Linter.Config[];
14
+
15
+ type ConfigItem = Linter.Config;
16
+ type OptionsConfig = {
17
+ tsconfigRootDir?: string;
18
+ ignores?: string[];
19
+ };
20
+
21
+ /**
22
+ * Construct an array of ESLint flat config items.
23
+ *
24
+ * @param options - The options for generating the ESLint configurations.
25
+ * @param userConfigs - Additional user configurations to be appended.
26
+ * @returns The composed ESLint configurations.
27
+ *
28
+ * @example
29
+ * ```js
30
+ * // eslint.config.js
31
+ * import createConfig from "@miskamyasa/eslint-config"
32
+ *
33
+ * export default createConfig({
34
+ * tsconfigRootDir: import.meta.dirname,
35
+ * })
36
+ * ```
37
+ */
38
+ declare function createConfig(options?: OptionsConfig, ...userConfigs: Linter.Config[]): Linter.Config[];
39
+
40
+ declare const GLOB_JS = "**/*.js";
41
+ declare const GLOB_CJS = "**/*.cjs";
42
+ declare const GLOB_TS = "**/*.ts";
43
+ declare const GLOB_TSX = "**/*.tsx";
44
+ declare const GLOB_SRC: string[];
45
+ declare const GLOB_EXCLUDE: string[];
46
+
47
+ export { type ConfigItem, GLOB_CJS, GLOB_EXCLUDE, GLOB_JS, GLOB_SRC, GLOB_TS, GLOB_TSX, type OptionsConfig, createConfig, createConfig as default, createImportsConfig as imports, javascript, react, stylistic, typescript };
package/dist/index.js ADDED
@@ -0,0 +1,388 @@
1
+ // src/configs/imports.ts
2
+ import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
3
+ import { flatConfigs as imports } from "eslint-plugin-import-x";
4
+ import unusedImports from "eslint-plugin-unused-imports";
5
+ function createImportsConfig() {
6
+ return [
7
+ {
8
+ ...imports.recommended,
9
+ name: "miskamyasa/imports/recommended"
10
+ },
11
+ {
12
+ ...imports.react,
13
+ name: "miskamyasa/imports/react"
14
+ },
15
+ {
16
+ name: "miskamyasa/imports/typescript",
17
+ settings: {
18
+ "import-x/resolver-next": [
19
+ createTypeScriptImportResolver()
20
+ ]
21
+ }
22
+ },
23
+ {
24
+ name: "miskamyasa/imports/rules",
25
+ plugins: {
26
+ "unused-imports": unusedImports
27
+ },
28
+ rules: {
29
+ "import-x/namespace": "off",
30
+ "import-x/newline-after-import": [
31
+ "error",
32
+ { count: 1 }
33
+ ],
34
+ "import-x/named": "off",
35
+ "import-x/no-cycle": [
36
+ "error",
37
+ { ignoreExternal: true, maxDepth: 10 }
38
+ ],
39
+ "import-x/no-duplicates": "error",
40
+ "import-x/no-useless-path-segments": "error",
41
+ "import-x/no-anonymous-default-export": "error",
42
+ "import-x/no-default-export": "error",
43
+ "import-x/order": [
44
+ "error",
45
+ {
46
+ alphabetize: { caseInsensitive: true, order: "asc" },
47
+ groups: ["builtin", "external", "internal", "parent", "sibling"],
48
+ "newlines-between": "always",
49
+ pathGroups: [{ group: "external", pattern: "react+(|-native)", position: "before" }],
50
+ pathGroupsExcludedImportTypes: ["react", "react-native"]
51
+ }
52
+ ],
53
+ "import-x/no-absolute-path": "error",
54
+ "unused-imports/no-unused-imports": "error",
55
+ "unused-imports/no-unused-vars": [
56
+ "error",
57
+ { vars: "all", varsIgnorePattern: "^_", args: "after-used", argsIgnorePattern: "^_" }
58
+ ]
59
+ }
60
+ }
61
+ ];
62
+ }
63
+
64
+ // src/globs.ts
65
+ var GLOB_JS = "**/*.js";
66
+ var GLOB_CJS = "**/*.cjs";
67
+ var GLOB_TS = "**/*.ts";
68
+ var GLOB_TSX = "**/*.tsx";
69
+ var GLOB_SRC = [
70
+ GLOB_JS,
71
+ GLOB_CJS,
72
+ GLOB_TS,
73
+ GLOB_TSX
74
+ ];
75
+ var GLOB_EXCLUDE = [
76
+ "**/node_modules",
77
+ "**/dist",
78
+ "**/package-lock.json",
79
+ "**/yarn.lock",
80
+ "**/pnpm-lock.yaml",
81
+ "**/bun.lockb"
82
+ ];
83
+
84
+ // src/configs/javascript.ts
85
+ function javascript() {
86
+ return [
87
+ {
88
+ name: "miskamyasa/javascript/overrides",
89
+ files: [GLOB_JS, GLOB_CJS],
90
+ languageOptions: {
91
+ globals: {
92
+ require: "readonly",
93
+ module: "readonly",
94
+ __dirname: "readonly"
95
+ }
96
+ },
97
+ rules: {
98
+ "@typescript-eslint/explicit-function-return-type": "off",
99
+ "@typescript-eslint/no-require-imports": "off",
100
+ "@typescript-eslint/no-var-requires": "off"
101
+ }
102
+ }
103
+ ];
104
+ }
105
+
106
+ // src/configs/react.ts
107
+ import reactPlugin from "@eslint-react/eslint-plugin";
108
+ import reactHooksPlugin from "eslint-plugin-react-hooks";
109
+ function react() {
110
+ return [
111
+ {
112
+ ...reactPlugin.configs["recommended-typescript"],
113
+ name: "miskamyasa/react/recommended-typescript"
114
+ },
115
+ {
116
+ name: "miskamyasa/react/rules",
117
+ plugins: {
118
+ "react-hooks": reactHooksPlugin
119
+ },
120
+ rules: {
121
+ "react-hooks/rules-of-hooks": "error",
122
+ "react-hooks/exhaustive-deps": "error"
123
+ }
124
+ }
125
+ ];
126
+ }
127
+
128
+ // src/configs/stylistic.ts
129
+ import stylisticPlugin from "@stylistic/eslint-plugin";
130
+ function stylistic() {
131
+ const stylistic2 = stylisticPlugin;
132
+ return [
133
+ {
134
+ ...stylistic2.configs["disable-legacy"],
135
+ name: "miskamyasa/stylistic/disable-legacy"
136
+ },
137
+ {
138
+ name: "miskamyasa/stylistic/rules",
139
+ plugins: {
140
+ "@stylistic": stylistic2
141
+ },
142
+ rules: {
143
+ "@stylistic/block-spacing": ["error", "never"],
144
+ "@stylistic/comma-dangle": ["error", "always-multiline"],
145
+ "@stylistic/indent": ["error", 2],
146
+ "@stylistic/jsx-closing-bracket-location": [
147
+ "error",
148
+ { "nonEmpty": "after-props", "selfClosing": "after-props" }
149
+ ],
150
+ "@stylistic/jsx-curly-spacing": [
151
+ "error",
152
+ {
153
+ "attributes": {
154
+ "allowMultiline": false
155
+ },
156
+ "children": true,
157
+ "when": "never"
158
+ }
159
+ ],
160
+ "@stylistic/jsx-first-prop-new-line": ["error", "multiline-multiprop"],
161
+ "@stylistic/jsx-indent-props": ["error", 2],
162
+ "@stylistic/jsx-max-props-per-line": ["error", { "maximum": 1 }],
163
+ "@stylistic/jsx-sort-props": [
164
+ "error",
165
+ {
166
+ "callbacksLast": true,
167
+ "ignoreCase": true,
168
+ "noSortAlphabetically": false,
169
+ "reservedFirst": true,
170
+ "shorthandFirst": true,
171
+ "shorthandLast": false
172
+ }
173
+ ],
174
+ "@stylistic/jsx-tag-spacing": [
175
+ "error",
176
+ {
177
+ "afterOpening": "never",
178
+ "beforeClosing": "allow",
179
+ "beforeSelfClosing": "allow",
180
+ "closingSlash": "never"
181
+ }
182
+ ],
183
+ "@stylistic/jsx-wrap-multilines": [
184
+ "error",
185
+ {
186
+ "arrow": "parens",
187
+ "assignment": "parens",
188
+ "condition": "ignore",
189
+ "declaration": "parens",
190
+ "logical": "ignore",
191
+ "prop": "ignore",
192
+ "return": "parens"
193
+ }
194
+ ],
195
+ "@stylistic/key-spacing": [
196
+ "error",
197
+ {
198
+ "afterColon": true,
199
+ "mode": "strict"
200
+ }
201
+ ],
202
+ "@stylistic/keyword-spacing": [
203
+ "error",
204
+ {
205
+ "after": true,
206
+ "before": true
207
+ }
208
+ ],
209
+ "@stylistic/linebreak-style": [
210
+ "error",
211
+ "unix"
212
+ ],
213
+ "@stylistic/max-len": [
214
+ "warn",
215
+ {
216
+ "code": 120
217
+ }
218
+ ],
219
+ "@stylistic/no-multiple-empty-lines": [
220
+ "error",
221
+ {
222
+ "max": 1,
223
+ "maxEOF": 0,
224
+ "maxBOF": 0
225
+ }
226
+ ],
227
+ "no-multi-spaces": "error",
228
+ "@stylistic/object-curly-newline": [
229
+ "error",
230
+ {
231
+ "consistent": true,
232
+ "multiline": true
233
+ }
234
+ ],
235
+ "@stylistic/object-curly-spacing": ["error", "never"],
236
+ "@stylistic/object-property-newline": [
237
+ "error",
238
+ { "allowAllPropertiesOnSameLine": true }
239
+ ],
240
+ "@stylistic/operator-linebreak": [
241
+ "error",
242
+ "before",
243
+ { "overrides": { "=": "after" } }
244
+ ],
245
+ "@stylistic/quotes": [
246
+ "error",
247
+ "double",
248
+ {
249
+ "avoidEscape": true
250
+ }
251
+ ],
252
+ "@stylistic/semi": [
253
+ "error",
254
+ "never"
255
+ ],
256
+ "@stylistic/space-before-blocks": [
257
+ "error",
258
+ "always"
259
+ ],
260
+ "@stylistic/space-infix-ops": "error",
261
+ "@stylistic/switch-colon-spacing": [
262
+ "error",
263
+ { "after": true, "before": false }
264
+ ],
265
+ "@stylistic/type-annotation-spacing": "error",
266
+ "@stylistic/member-delimiter-style": [
267
+ "error",
268
+ {
269
+ "multilineDetection": "brackets",
270
+ "multiline": {
271
+ "delimiter": "comma",
272
+ "requireLast": true
273
+ },
274
+ "singleline": {
275
+ "delimiter": "comma",
276
+ "requireLast": false
277
+ },
278
+ "overrides": {
279
+ "interface": {
280
+ "multiline": {
281
+ "delimiter": "none"
282
+ },
283
+ "singleline": {
284
+ "delimiter": "comma"
285
+ }
286
+ }
287
+ }
288
+ }
289
+ ],
290
+ "comma-spacing": ["error", { "before": false, "after": true }],
291
+ "no-console": ["warn", { allow: ["warn", "error"] }],
292
+ "brace-style": ["error", "stroustrup"],
293
+ "no-use-before-define": "error"
294
+ }
295
+ }
296
+ ];
297
+ }
298
+
299
+ // src/configs/typescript.ts
300
+ import { configs } from "typescript-eslint";
301
+ function typescript(options) {
302
+ return [
303
+ ...configs.recommended,
304
+ ...configs.strictTypeChecked,
305
+ ...configs.stylisticTypeChecked,
306
+ {
307
+ name: "miskamyasa/typescript/language-options",
308
+ languageOptions: {
309
+ parserOptions: {
310
+ projectService: true,
311
+ tsconfigRootDir: options.tsconfigRootDir
312
+ }
313
+ }
314
+ },
315
+ {
316
+ name: "miskamyasa/typescript/rules",
317
+ rules: {
318
+ "@typescript-eslint/consistent-type-definitions": ["error", "type"],
319
+ "@typescript-eslint/no-unused-vars": "off",
320
+ "@typescript-eslint/restrict-template-expressions": [
321
+ "error",
322
+ {
323
+ allowNumber: true,
324
+ allowBoolean: false,
325
+ allowAny: false,
326
+ allowNullish: false,
327
+ allowRegExp: false
328
+ }
329
+ ],
330
+ "@typescript-eslint/no-misused-promises": [
331
+ "error",
332
+ { checksVoidReturn: { attributes: false } }
333
+ ]
334
+ }
335
+ }
336
+ ];
337
+ }
338
+
339
+ // src/factory.ts
340
+ import eslint from "@eslint/js";
341
+ function createConfig(options = {}, ...userConfigs) {
342
+ const {
343
+ tsconfigRootDir = process.cwd(),
344
+ ignores = []
345
+ } = options;
346
+ return [
347
+ // Global ignores
348
+ {
349
+ name: "miskamyasa/ignores",
350
+ ignores: [
351
+ ...GLOB_EXCLUDE,
352
+ ...ignores
353
+ ]
354
+ },
355
+ // ESLint recommended
356
+ {
357
+ ...eslint.configs.recommended,
358
+ name: "miskamyasa/eslint/recommended"
359
+ },
360
+ // TypeScript
361
+ ...typescript({ tsconfigRootDir }),
362
+ // Imports
363
+ ...createImportsConfig(),
364
+ // React
365
+ ...react(),
366
+ // Stylistic
367
+ ...stylistic(),
368
+ // JavaScript/CJS overrides (must come after TypeScript rules)
369
+ ...javascript(),
370
+ // User configs (appended last for maximum override power)
371
+ ...userConfigs
372
+ ];
373
+ }
374
+ export {
375
+ GLOB_CJS,
376
+ GLOB_EXCLUDE,
377
+ GLOB_JS,
378
+ GLOB_SRC,
379
+ GLOB_TS,
380
+ GLOB_TSX,
381
+ createConfig,
382
+ createConfig as default,
383
+ createImportsConfig as imports,
384
+ javascript,
385
+ react,
386
+ stylistic,
387
+ typescript
388
+ };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@miskamyasa/eslint-config",
3
+ "type": "module",
4
+ "version": "1.0.1",
5
+ "description": "Opinionated ESLint config for React + TypeScript projects",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "types": "./dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "sideEffects": false,
17
+ "keywords": [
18
+ "eslint",
19
+ "eslint-config",
20
+ "typescript",
21
+ "react",
22
+ "stylistic"
23
+ ],
24
+ "author": "Denis <MiskaMyasa> Zaitsev",
25
+ "license": "MIT",
26
+ "engines": {
27
+ "node": ">=22.0.0"
28
+ },
29
+ "dependencies": {
30
+ "@eslint-react/eslint-plugin": "2.13.0",
31
+ "@eslint/js": "10.0.1",
32
+ "@stylistic/eslint-plugin": "5.10.0",
33
+ "eslint-import-resolver-typescript": "4.4.4",
34
+ "eslint-plugin-import-x": "4.16.2",
35
+ "eslint-plugin-react-hooks": "7.0.1",
36
+ "eslint-plugin-unused-imports": "4.4.1",
37
+ "typescript-eslint": "8.57.0"
38
+ },
39
+ "peerDependencies": {
40
+ "eslint": ">=9.0.0",
41
+ "typescript": ">=5.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "25.5.0",
45
+ "eslint": "10.0.3",
46
+ "jiti": "2.6.1",
47
+ "tsup": "8.5.1",
48
+ "typescript": "5.9.3"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup",
52
+ "lint": "eslint .",
53
+ "typecheck": "tsc --noEmit"
54
+ }
55
+ }