@1stg/eslint-config 8.5.0 → 9.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Change Log
2
2
 
3
+ ## 9.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#341](https://github.com/1stG/configs/pull/341) [`8527c5f`](https://github.com/1stG/configs/commit/8527c5f5fd264216a7244dcb6b8aa8f7ff0dd23f) Thanks [@JounQin](https://github.com/JounQin)! - feat(eslint-config)!: mark all framework plugins as optional peer
8
+
3
9
  ## 8.5.0
4
10
 
5
11
  ### Minor Changes
package/_util.js CHANGED
@@ -8,14 +8,14 @@ import {
8
8
  isPkgAvailable,
9
9
  getMonorepoPkgs,
10
10
  tryRequirePkg,
11
+ tryFile,
12
+ tryPkg,
11
13
  } from '@pkgr/utils'
12
14
  import configPrettier from 'eslint-config-prettier'
13
15
  import prettierRecommended from 'eslint-plugin-prettier/recommended'
14
16
  import tseslint from 'typescript-eslint'
15
17
 
16
- /**
17
- * @type {string[]}
18
- */
18
+ /** @type {string[]} */
19
19
  let allowModules = []
20
20
 
21
21
  if (isMonorepo()) {
@@ -38,7 +38,8 @@ export { allowModules }
38
38
 
39
39
  export const isTsAvailable = isPkgAvailable('typescript')
40
40
 
41
- export const isWebpackAvailable = isPkgAvailable('webpack')
41
+ export const isWebpackAvailable =
42
+ isPkgAvailable('webpack') || isPkgAvailable('@rspack/core')
42
43
 
43
44
  export const webpackSpecVars = [
44
45
  '__non_webpack_require__',
@@ -61,3 +62,15 @@ export const magicNumbers = [
61
62
  export const prettierExtends = tseslint.config(
62
63
  preferPrettier ? configPrettier : prettierRecommended,
63
64
  )
65
+
66
+ export const project =
67
+ tryFile('tsconfig.eslint.json') ||
68
+ tryFile('tsconfig.base.json') ||
69
+ tryFile('tsconfig.json') ||
70
+ tryPkg('@1stg/tsconfig')
71
+
72
+ export const nonSourceRules = /** @type {const} */ ({
73
+ 'n/no-extraneous-import': 0,
74
+ 'n/no-extraneous-require': 0,
75
+ 'n/no-unsupported-features/es-builtins': 0,
76
+ })
package/angular.js ADDED
@@ -0,0 +1,48 @@
1
+ // @ts-check
2
+
3
+ import { preferPrettier } from '@1stg/config'
4
+ import angular_ from 'angular-eslint'
5
+ import angularTemplateParser from 'angular-eslint-template-parser'
6
+ import * as markup_ from 'eslint-plugin-markup'
7
+ import tseslint from 'typescript-eslint'
8
+
9
+ import { prettierExtends, project } from './_util.js'
10
+
11
+ export const angular = tseslint.config(
12
+ {
13
+ name: '@1stg/angular-ts',
14
+ files: ['**/*.ts'],
15
+ ignores: ['**/*.d.ts'],
16
+ languageOptions: {
17
+ parserOptions: {
18
+ project,
19
+ },
20
+ },
21
+ extends: [angular_.configs.tsRecommended, prettierExtends],
22
+ processor: angular_.processInlineTemplates,
23
+ rules: {
24
+ '@angular-eslint/prefer-on-push-component-change-detection': 1,
25
+ },
26
+ },
27
+ {
28
+ name: '@1stg/angular-template',
29
+ files: ['**/*.html'],
30
+ extends: [
31
+ angular_.configs.templateRecommended,
32
+ markup_.configs.flatRecommended,
33
+ prettierExtends,
34
+ ],
35
+ languageOptions: {
36
+ parser: angularTemplateParser,
37
+ },
38
+ rules: {
39
+ '@angular-eslint/template/eqeqeq': [2, { allowNullOrUndefined: true }],
40
+ 'prettier/prettier': preferPrettier ? 0 : [2, { parser: 'angular' }],
41
+ 'spaced-comment': 0,
42
+ },
43
+ },
44
+ {
45
+ files: ['**/*inline-template-*.component.html'],
46
+ rules: { 'unicorn/no-empty-file': 0 },
47
+ },
48
+ )
package/jest.js ADDED
@@ -0,0 +1,17 @@
1
+ // @ts-check
2
+
3
+ import jest_ from 'eslint-plugin-jest'
4
+ import globals from 'globals'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ import { test } from './test.js'
8
+
9
+ export const jest = tseslint.config({
10
+ name: '@1stg/jest',
11
+ files: ['**/*.{spec,test}.{js,jsx,ts,tsx}'],
12
+ extends: [jest_.configs['flat/recommended']],
13
+ languageOptions: {
14
+ globals: globals.jest,
15
+ },
16
+ rules: test.rules,
17
+ })
package/js-base.js ADDED
@@ -0,0 +1,47 @@
1
+ // @ts-check
2
+
3
+ /** @import {TSESLint} from '@typescript-eslint/utils' */
4
+
5
+ import { tryFile, tryPkg, tryRequirePkg } from '@pkgr/utils'
6
+
7
+ /** @type {string | undefined} */
8
+ let configFile
9
+
10
+ export const isBabelAvailable =
11
+ tryPkg('@babel/eslint-parser') && tryPkg('@babel/eslint-plugin')
12
+
13
+ /** @satisfies {TSESLint.FlatConfig.Config} */
14
+ export const jsBase = {
15
+ name: '@1stg/js-base',
16
+ files: ['**/*.{cjs,mjs,js,jsx}'],
17
+ languageOptions: isBabelAvailable
18
+ ? {
19
+ parser: tryRequirePkg('@babel/eslint-parser'),
20
+ // eslint-disable-next-line no-cond-assign, sonarjs/no-nested-assignment
21
+ parserOptions: (configFile =
22
+ tryFile('babel.config.js') ||
23
+ tryFile('.babelrc.js') ||
24
+ tryPkg('@1stg/babel-preset/config'))
25
+ ? { babelOptions: { configFile } }
26
+ : { requireConfigFile: false },
27
+ }
28
+ : {},
29
+ plugins: isBabelAvailable
30
+ ? {
31
+ '@babel': /** @type {Omit<TSESLint.FlatConfig.Plugin, 'configs'>} */ (
32
+ tryRequirePkg('@babel/eslint-plugin')
33
+ ),
34
+ }
35
+ : undefined,
36
+ rules: {
37
+ camelcase: [2, { properties: 'never', ignoreDestructuring: true }],
38
+ ...(isBabelAvailable && {
39
+ 'new-cap': 0,
40
+ 'no-invalid-this': 0,
41
+ 'no-unused-expressions': 0,
42
+ '@babel/new-cap': 2,
43
+ '@babel/no-invalid-this': 2,
44
+ '@babel/no-unused-expressions': 2,
45
+ }),
46
+ },
47
+ }
package/js.js ADDED
@@ -0,0 +1,22 @@
1
+ // @ts-check
2
+
3
+ import jsdoc from 'eslint-plugin-jsdoc'
4
+ import tseslint from 'typescript-eslint'
5
+
6
+ import { isTsAvailable } from './_util.js'
7
+ import { jsBase } from './js-base.js'
8
+
9
+ const jsdocConfig = jsdoc.configs['flat/recommended']
10
+
11
+ export const js = tseslint.config({
12
+ ...jsBase,
13
+ name: '@1stg/js',
14
+ extends: [jsdocConfig],
15
+ rules: {
16
+ ...jsBase.rules,
17
+ 'jsdoc/require-jsdoc': 0,
18
+ 'jsdoc/require-param-description': 0,
19
+ 'jsdoc/tag-lines': [1, 'never', { startLines: 1 }],
20
+ },
21
+ settings: isTsAvailable ? { jsdoc: { mode: 'typescript' } } : undefined,
22
+ })
package/overrides.js CHANGED
@@ -1,250 +1,35 @@
1
1
  // @ts-check
2
2
 
3
- /**
4
- * @import {Linter} from 'eslint'
5
- */
6
-
7
- import path from 'node:path'
3
+ /** @import {TSESLint} from '@typescript-eslint/utils' */
8
4
 
9
5
  import { jsoncFiles, nonJsonRcFiles, preferPrettier } from '@1stg/config'
10
- import babelParser from '@babel/eslint-parser'
11
- import babel from '@babel/eslint-plugin'
12
- import react_ from '@eslint-react/eslint-plugin'
13
- import { isPkgAvailable, tryFile, tryPkg } from '@pkgr/utils'
14
- import vitest_ from '@vitest/eslint-plugin'
15
- import angular_ from 'angular-eslint'
16
- import angularTemplateParser from 'angular-eslint-template-parser'
17
- import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
6
+ import { isPkgAvailable } from '@pkgr/utils'
18
7
  import * as eslintMdx from 'eslint-mdx'
19
- import importX from 'eslint-plugin-import-x'
20
- import jest_ from 'eslint-plugin-jest'
21
- import jsdoc from 'eslint-plugin-jsdoc'
22
8
  import jsonc_ from 'eslint-plugin-jsonc'
23
9
  import * as markup_ from 'eslint-plugin-markup'
24
10
  import * as mdx_ from 'eslint-plugin-mdx'
25
- import * as reactHooks_ from 'eslint-plugin-react-hooks'
26
11
  import toml_ from 'eslint-plugin-toml'
27
- import vue_ from 'eslint-plugin-vue'
28
12
  import yml_ from 'eslint-plugin-yml'
29
- import globals from 'globals'
30
13
  import tseslint from 'typescript-eslint'
31
- import vueParser from 'vue-eslint-parser'
32
-
33
- import { isTsAvailable, magicNumbers, prettierExtends } from './_util.js'
34
-
35
- /**
36
- * @import {TSESLint} from '@typescript-eslint/utils'
37
- */
38
14
 
39
- const configFile =
40
- tryFile(path.resolve('babel.config.js')) ||
41
- tryFile(path.resolve('.babelrc.js')) ||
42
- tryPkg('@1stg/babel-preset/config')
43
-
44
- /**
45
- * @satisfies {TSESLint.FlatConfig.Config}
46
- */
47
- const jsBase = {
48
- name: '@1stg/js-base',
49
- files: ['**/*.{cjs,mjs,js,jsx}'],
50
- languageOptions: {
51
- parser: babelParser,
52
- parserOptions: configFile
53
- ? { babelOptions: { configFile } }
54
- : { requireConfigFile: false },
55
- },
56
- plugins: {
57
- '@babel': babel,
58
- },
59
- rules: {
60
- camelcase: [
61
- 2,
62
- {
63
- properties: 'never',
64
- ignoreDestructuring: true,
65
- },
66
- ],
67
- 'new-cap': 0,
68
- 'no-invalid-this': 0,
69
- 'no-unused-expressions': 0,
70
- '@babel/new-cap': 2,
71
- '@babel/no-invalid-this': 2,
72
- '@babel/no-unused-expressions': 2,
73
- },
74
- }
75
-
76
- const jsdocConfig = jsdoc.configs['flat/recommended']
77
-
78
- export const js = tseslint.config({
79
- ...jsBase,
80
- name: '@1stg/js',
81
- extends: [jsdocConfig],
82
- rules: {
83
- ...jsBase.rules,
84
- 'jsdoc/require-jsdoc': 0,
85
- 'jsdoc/require-param-description': 0,
86
- 'jsdoc/tag-lines': [
87
- 1,
88
- 'never',
89
- {
90
- startLines: 1,
91
- },
92
- ],
93
- },
94
- settings: isTsAvailable
95
- ? {
96
- jsdoc: {
97
- mode: 'typescript',
98
- },
99
- }
100
- : undefined,
101
- })
102
-
103
- const project =
104
- tryFile(path.resolve('tsconfig.eslint.json')) ||
105
- tryFile(path.resolve('tsconfig.base.json')) ||
106
- tryFile(path.resolve('tsconfig.json')) ||
107
- tryPkg('@1stg/tsconfig')
108
-
109
- const resolveSettings = {
110
- 'import-x/external-module-folders': [
111
- 'node_modules',
112
- 'node_modules/@d-ts',
113
- 'node_modules/@types',
114
- ],
115
- 'import-x/resolver-next': [
116
- createTypeScriptImportResolver({
117
- alwaysTryTypes: true,
118
- project,
119
- }),
120
- ],
121
- }
122
-
123
- const tsBase = tseslint.config({
124
- name: '@1stg/ts-base',
125
- files: ['**/*.{cts,mts,ts,tsx}'],
126
- languageOptions: {
127
- parserOptions: {
128
- project,
129
- },
130
- },
131
- extends: [
132
- tseslint.configs.eslintRecommended,
133
- tseslint.configs.recommended,
134
- importX.flatConfigs.typescript,
135
- prettierExtends,
136
- ],
137
- settings: resolveSettings,
138
- rules: {
139
- '@typescript-eslint/adjacent-overload-signatures': 2,
140
- '@typescript-eslint/array-type': [
141
- 2,
142
- {
143
- default: 'array-simple',
144
- },
145
- ],
146
- '@typescript-eslint/ban-ts-comment': 0,
147
- '@typescript-eslint/consistent-type-definitions': [2, 'interface'],
148
- '@typescript-eslint/explicit-function-return-type': 0,
149
- '@typescript-eslint/explicit-member-accessibility': [
150
- 2,
151
- {
152
- accessibility: 'no-public',
153
- overrides: {
154
- parameterProperties: 'off',
155
- },
156
- },
157
- ],
158
- '@typescript-eslint/explicit-module-boundary-types': 0,
159
- '@typescript-eslint/member-ordering': 2,
160
- // eslint-disable-line sonarjs/todo-tag -- TODO: find better config
161
- '@typescript-eslint/naming-convention': 0,
162
- '@typescript-eslint/no-empty-function': 2,
163
- '@typescript-eslint/no-extraneous-class': [
164
- 2,
165
- {
166
- allowWithDecorator: true,
167
- },
168
- ],
169
- '@typescript-eslint/no-for-in-array': 2,
170
- '@typescript-eslint/no-non-null-assertion': 0,
171
- '@typescript-eslint/no-parameter-properties': 0,
172
- '@typescript-eslint/no-require-imports': 2,
173
- '@typescript-eslint/no-this-alias': [
174
- 2,
175
- {
176
- allowDestructuring: true,
177
- allowedNames: ['self'],
178
- },
179
- ],
180
- '@typescript-eslint/no-use-before-define': [
181
- 2,
182
- {
183
- classes: false,
184
- functions: false,
185
- },
186
- ],
187
- '@typescript-eslint/no-useless-constructor': 2,
188
- '@typescript-eslint/no-unused-vars': [
189
- 2,
190
- {
191
- argsIgnorePattern: '^_',
192
- varsIgnorePattern: '^_',
193
- },
194
- ],
195
- '@typescript-eslint/prefer-for-of': 2,
196
- '@typescript-eslint/prefer-function-type': 2,
197
- '@typescript-eslint/prefer-ts-expect-error': 2,
198
- '@typescript-eslint/sort-type-constituents': 2,
199
- '@typescript-eslint/triple-slash-reference': [
200
- 2,
201
- {
202
- lib: 'never',
203
- path: 'always',
204
- types: 'prefer-import',
205
- },
206
- ],
207
- '@typescript-eslint/unified-signatures': 2,
208
- camelcase: 0,
209
- 'import-x/default': 0,
210
- 'import-x/named': 0,
211
- 'import-x/namespace': 0,
212
- 'import-x/no-duplicates': 2,
213
- 'import-x/no-named-as-default': 0,
214
- 'import-x/no-named-as-default-member': 0,
215
- 'no-empty-function': 0,
216
- 'no-restricted-syntax': [
217
- 2,
218
- {
219
- selector:
220
- 'TSTypeAliasDeclaration > .typeAnnotation[type=/^(TS.*Keyword|TSTypeReference)$/]:not(:has(TSTypeParameterInstantiation)):not(:has(TSQualifiedName))',
221
- message:
222
- 'Type alias references simple `Ts*Keyword`s or `TSTypeReference` only is not allowed',
223
- },
224
- ],
225
- 'no-unused-vars': 0,
226
- 'no-use-before-define': 0,
227
- 'no-useless-constructor': 0,
228
- // covered by @typescript-eslint/no-floating-promises
229
- 'promise/always-return': 0,
230
- 'promise/catch-or-return': 0,
231
- // ts itself has guaranteed it
232
- 'unicorn/no-array-callback-reference': 0,
233
- // covered by @typescript-eslint/no-extraneous-class
234
- 'unicorn/no-static-only-class': 0,
235
- // covered by @typescript-eslint/no-this-alias
236
- 'unicorn/no-this-assignment': 0,
237
- },
238
- })
15
+ import {
16
+ isTsAvailable,
17
+ magicNumbers,
18
+ nonSourceRules,
19
+ prettierExtends,
20
+ } from './_util.js'
21
+ import { jsBase } from './js-base.js'
22
+ import { js } from './js.js'
23
+ import { reactJsx } from './react.js'
24
+ import { test } from './test.js'
25
+ import { resolveSettings, tsBase } from './ts-base.js'
239
26
 
240
27
  export const ts = tseslint.config(
241
28
  tsBase,
242
29
  {
243
30
  name: '@1stg/cli',
244
31
  files: ['**/{bin,cli}.ts'],
245
- rules: {
246
- 'n/hashbang': 0,
247
- },
32
+ rules: { 'n/hashbang': 0 },
248
33
  },
249
34
  {
250
35
  name: '@1stg/code-block',
@@ -265,12 +50,7 @@ export const ts = tseslint.config(
265
50
  ],
266
51
  extends: [tseslint.configs.recommendedTypeChecked],
267
52
  rules: {
268
- '@typescript-eslint/no-floating-promises': [
269
- 2,
270
- {
271
- ignoreVoid: true,
272
- },
273
- ],
53
+ '@typescript-eslint/no-floating-promises': [2, { ignoreVoid: true }],
274
54
  '@typescript-eslint/no-magic-numbers': [
275
55
  2,
276
56
  {
@@ -291,9 +71,7 @@ export const ts = tseslint.config(
291
71
  '@typescript-eslint/prefer-string-starts-ends-with': 2,
292
72
  '@typescript-eslint/require-array-sort-compare': [
293
73
  2,
294
- {
295
- ignoreStringArrays: true,
296
- },
74
+ { ignoreStringArrays: true },
297
75
  ],
298
76
  '@typescript-eslint/restrict-plus-operands': 2,
299
77
  'no-constant-condition': 0,
@@ -308,9 +86,7 @@ export const ts = tseslint.config(
308
86
  },
309
87
  )
310
88
 
311
- /**
312
- * @satisfies {TSESLint.FlatConfig.Config}
313
- */
89
+ /** @satisfies {TSESLint.FlatConfig.Config} */
314
90
  export const dTs = {
315
91
  name: '@1stg/d-ts',
316
92
  files: ['**/*.d.{cts,mts,ts}', '**/*.d.*.{cts,mts,ts}'],
@@ -324,168 +100,13 @@ export const dTs = {
324
100
  },
325
101
  }
326
102
 
327
- const reactJsx = tseslint.config({
328
- name: '@1stg/react-jsx',
329
- extends: [react_.configs.recommended, prettierExtends],
330
- settings: {
331
- react: {
332
- version: 'detect',
333
- },
334
- },
335
- })
336
-
337
- export const react = tseslint.config(
338
- {
339
- name: '@1stg/react',
340
- files: ['**/*.{js,jsx,tsx}'],
341
- extends: [reactJsx],
342
- rules: {
343
- '@eslint-react/avoid-shorthand-boolean': 2,
344
- // '@eslint-react/naming-convention/handler-prop': [
345
- // 2,
346
- // {
347
- // eventHandlerPrefix: false,
348
- // eventHandlerPropPrefix: 'on',
349
- // },
350
- // ],
351
- 'sonarjs/function-name': isTsAvailable
352
- ? [2, { format: String.raw`^_?[a-zA-Z][a-zA-Z0-9]*\$?$` }]
353
- : 0,
354
- },
355
- },
356
- {
357
- name: '@1stg/react-tsx',
358
- files: ['**/*.tsx'],
359
- rules: {
360
- '@eslint-react/display-name': 0,
361
- },
362
- },
363
- {
364
- name: '@1stg/react-hooks',
365
- files: ['**/*.{js,jsx,ts,tsx}'],
366
- plugins: { 'react-hooks': reactHooks_ },
367
- rules: {
368
- 'react-hooks/rules-of-hooks': 2,
369
- 'react-hooks/exhaustive-deps': 2,
370
- },
371
- },
372
- {
373
- name: '@1stg/react-ts',
374
- files: ['**/*.{ts,tsx}'],
375
- rules: {
376
- 'no-restricted-imports': [2, 'prop-types'],
377
- '@eslint-react/prop-types': 0,
378
- },
379
- },
380
- )
381
-
382
- const vueExtends = tseslint.config(
383
- vue_.configs['flat/recommended'],
384
- prettierExtends,
385
- )
386
-
387
- export const vue = tseslint.config(
388
- {
389
- ...jsBase,
390
- name: '@1stg/js-vue',
391
- files: /** @type {string[]} */ (
392
- [...jsBase.files, !isTsAvailable && '**/*.vue'].filter(Boolean)
393
- ),
394
- languageOptions: {
395
- parser: vueParser,
396
- parserOptions: {
397
- ...jsBase.languageOptions.parserOptions,
398
- parser: babelParser,
399
- },
400
- },
401
- extends: vueExtends,
402
- },
403
- isTsAvailable
404
- ? {
405
- name: '@1stg/ts-vue',
406
- extends: [...tsBase, ...vueExtends],
407
- files: [.../** @type {string[]} */ (tsBase[0].files), '**/*.vue'],
408
- languageOptions: {
409
- parser: vueParser,
410
- parserOptions: {
411
- parser: tseslint.parser,
412
- extraFileExtensions: ['.vue'],
413
- },
414
- },
415
- }
416
- : [],
417
- {
418
- name: '@1stg/vue',
419
- files: ['**/*.vue'],
420
- extends: [
421
- // 'plugin:markup/recommended',
422
- ...vueExtends,
423
- ],
424
- rules: {
425
- 'no-unused-vars': 0,
426
- '@typescript-eslint/no-unused-vars': 0,
427
- },
428
- },
429
- )
430
-
431
- export const angular = tseslint.config(
432
- {
433
- name: '@1stg/angular-ts',
434
- files: ['**/*.ts'],
435
- ignores: ['**/*.d.ts'],
436
- languageOptions: {
437
- parserOptions: {
438
- project,
439
- },
440
- },
441
- extends: [angular_.configs.tsRecommended, prettierExtends],
442
- processor: angular_.processInlineTemplates,
443
- rules: {
444
- '@angular-eslint/prefer-on-push-component-change-detection': 1,
445
- },
446
- },
447
- {
448
- name: '@1stg/angular-template',
449
- files: ['**/*.html'],
450
- extends: [
451
- angular_.configs.templateRecommended,
452
- markup_.configs.flatRecommended,
453
- prettierExtends,
454
- ],
455
- languageOptions: {
456
- parser: angularTemplateParser,
457
- },
458
- rules: {
459
- '@angular-eslint/template/eqeqeq': [
460
- 2,
461
- {
462
- allowNullOrUndefined: true,
463
- },
464
- ],
465
- 'prettier/prettier': preferPrettier ? 0 : [2, { parser: 'angular' }],
466
- 'spaced-comment': 0,
467
- },
468
- },
469
- {
470
- files: ['**/*inline-template-*.component.html'],
471
- rules: {
472
- 'unicorn/no-empty-file': 0,
473
- },
474
- },
475
- )
476
-
477
103
  export const markup = tseslint.config(
478
104
  {
479
105
  name: '@1stg/markup-html',
480
106
  files: ['**/*.html'],
481
107
  extends: [markup_.configs.flatRecommended],
482
108
  rules: {
483
- 'prettier/prettier': [
484
- preferPrettier ? 0 : 2,
485
- {
486
- parser: 'html',
487
- },
488
- ],
109
+ 'prettier/prettier': [preferPrettier ? 0 : 2, { parser: 'html' }],
489
110
  },
490
111
  },
491
112
  {
@@ -524,46 +145,7 @@ export const mdx = tseslint.config(
524
145
  mdx_.configs.flatCodeBlocks,
525
146
  )
526
147
 
527
- const nonSourceRules = /** @type {const} */ ({
528
- 'n/no-extraneous-import': 0,
529
- 'n/no-extraneous-require': 0,
530
- 'n/no-unsupported-features/es-builtins': 0,
531
- })
532
-
533
- /**
534
- * @satisfies {TSESLint.FlatConfig.Config}
535
- */
536
- export const test = {
537
- name: '@1stg/test',
538
- files: ['**/{__test__,test,tests}/**/*'],
539
- rules: nonSourceRules,
540
- }
541
-
542
- export const tests = test
543
-
544
- export const jest = tseslint.config({
545
- name: '@1stg/jest',
546
- files: ['**/*.{spec,test}.{js,jsx,ts,tsx}'],
547
- extends: [jest_.configs['flat/recommended']],
548
- languageOptions: {
549
- globals: globals.jest,
550
- },
551
- rules: test.rules,
552
- })
553
-
554
- export const vitest = tseslint.config({
555
- name: '@1stg/vitest',
556
- files: ['**/*.{spec,test}.{js,jsx,ts,tsx}'],
557
- extends: [vitest_.configs.recommended],
558
- languageOptions: {
559
- globals: globals.vitest,
560
- },
561
- rules: test.rules,
562
- })
563
-
564
- /**
565
- * @satisfies {TSESLint.FlatConfig.Config}
566
- */
148
+ /** @satisfies {TSESLint.FlatConfig.Config} */
567
149
  export const script = {
568
150
  name: '@1stg/script',
569
151
  files: ['**/scripts/**/*'],
@@ -572,9 +154,7 @@ export const script = {
572
154
 
573
155
  export const scripts = script
574
156
 
575
- /**
576
- * @satisfies {TSESLint.FlatConfig.Config}
577
- */
157
+ /** @satisfies {TSESLint.FlatConfig.Config} */
578
158
  export const story = {
579
159
  name: '@1stg/story',
580
160
  files: ['**/.storybook/**/*', '**/stories/**/*'],
@@ -583,9 +163,7 @@ export const story = {
583
163
 
584
164
  export const stories = story
585
165
 
586
- /**
587
- * @satisfies {TSESLint.FlatConfig.Config}
588
- */
166
+ /** @satisfies {TSESLint.FlatConfig.Config} */
589
167
  export const config = {
590
168
  name: '@1stg/config',
591
169
  files: ['**/.*.js', '**/*.config.{js,ts}'],
@@ -635,20 +213,28 @@ export const yaml = tseslint.config({
635
213
 
636
214
  export const yml = yaml
637
215
 
638
- const isReactAvailable = isPkgAvailable('react')
639
-
640
216
  export const overrides = tseslint.config(
641
- isPkgAvailable('@babel/core') ? js : [],
217
+ js,
642
218
  isTsAvailable ? ts : [],
643
- isReactAvailable ? react : [],
219
+ isPkgAvailable('@eslint-react/eslint-plugin')
220
+ ? await import('./react.js').then(({ react }) => react)
221
+ : [],
644
222
  // The order matters, the later should to be preferred
645
223
  markup,
646
- isPkgAvailable('vue') ? vue : [],
647
- isPkgAvailable('@angular/core') ? angular : [],
224
+ isPkgAvailable('eslint-plugin-vue')
225
+ ? await import('./vue.js').then(({ vue }) => vue)
226
+ : [],
227
+ isPkgAvailable('angular-eslint')
228
+ ? await import('./angular.js').then(({ angular }) => angular)
229
+ : [],
648
230
  md,
649
231
  mdx,
650
- isPkgAvailable('jest') ? jest : [],
651
- isPkgAvailable('vitest') ? vitest : [],
232
+ isPkgAvailable('eslint-plugin-jest')
233
+ ? await import('./jest.js').then(({ jest }) => jest)
234
+ : [],
235
+ isPkgAvailable('@vitest/eslint-plugin')
236
+ ? await import('./vitest.js').then(({ vitest }) => vitest)
237
+ : [],
652
238
  test,
653
239
  scripts,
654
240
  stories,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1stg/eslint-config",
3
- "version": "8.5.0",
3
+ "version": "9.0.0",
4
4
  "type": "module",
5
5
  "description": "Personal but Shareable ESLint Configuration for all 1stG.me projects",
6
6
  "repository": "https://github.com/1stG/configs.git",
@@ -21,26 +21,57 @@
21
21
  "./recommended": "./recommended.js"
22
22
  },
23
23
  "peerDependencies": {
24
- "eslint": ">=8.0.0"
24
+ "@babel/eslint-parser": "^7.0.0",
25
+ "@babel/eslint-plugin": "^7.0.0",
26
+ "@eslint-react/eslint-plugin": "^1.0.0",
27
+ "@vitest/eslint-plugin": "^1.0.0",
28
+ "angular-eslint": "^19.0.0",
29
+ "angular-eslint-template-parser": "^0.1.0",
30
+ "eslint": ">=8.0.0",
31
+ "eslint-plugin-jest": "^28.0.0",
32
+ "eslint-plugin-react-hooks": "^5.0.0",
33
+ "eslint-plugin-vue": "^10.0.0"
34
+ },
35
+ "peerDependenciesMeta": {
36
+ "@babel/eslint-parser": {
37
+ "optional": true
38
+ },
39
+ "@babel/eslint-plugin": {
40
+ "optional": true
41
+ },
42
+ "@eslint-react/eslint-plugin": {
43
+ "optional": true
44
+ },
45
+ "@vitest/eslint-plugin": {
46
+ "optional": true
47
+ },
48
+ "angular-eslint": {
49
+ "optional": true
50
+ },
51
+ "angular-eslint-template-parser": {
52
+ "optional": true
53
+ },
54
+ "eslint-plugin-jest": {
55
+ "optional": true
56
+ },
57
+ "eslint-plugin-react-hooks": {
58
+ "optional": true
59
+ },
60
+ "eslint-plugin-vue": {
61
+ "optional": true
62
+ }
25
63
  },
26
64
  "dependencies": {
27
65
  "@1stg/config": "^1.0.4",
28
- "@babel/eslint-parser": "^7.27.0",
29
- "@babel/eslint-plugin": "^7.27.0",
30
66
  "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
31
- "@eslint-react/eslint-plugin": "^1.46.0",
32
67
  "@eslint/js": "^9.24.0",
33
68
  "@pkgr/utils": "^3.1.1",
34
69
  "@typescript-eslint/eslint-plugin": "^8.29.1",
35
70
  "@typescript-eslint/parser": "^8.29.1",
36
- "@vitest/eslint-plugin": "^1.1.42",
37
- "angular-eslint": "^19.3.0",
38
- "angular-eslint-template-parser": "^0.1.3",
39
71
  "eslint-config-prettier": "^10.1.2",
40
72
  "eslint-import-resolver-typescript": "^4.3.2",
41
73
  "eslint-plugin-css": "^0.11.0",
42
74
  "eslint-plugin-import-x": "^4.10.3",
43
- "eslint-plugin-jest": "^28.11.0",
44
75
  "eslint-plugin-jsdoc": "^50.6.9",
45
76
  "eslint-plugin-jsonc": "^2.20.0",
46
77
  "eslint-plugin-markup": "^1.0.0",
@@ -48,13 +79,11 @@
48
79
  "eslint-plugin-n": "^17.17.0",
49
80
  "eslint-plugin-prettier": "^5.2.6",
50
81
  "eslint-plugin-promise": "^7.2.1",
51
- "eslint-plugin-react-hooks": "^5.2.0",
52
82
  "eslint-plugin-regexp": "^2.7.0",
53
83
  "eslint-plugin-simple-import-sort": "^12.1.1",
54
84
  "eslint-plugin-sonarjs": "^3.0.2",
55
85
  "eslint-plugin-toml": "^0.12.0",
56
86
  "eslint-plugin-unicorn": "^58.0.0",
57
- "eslint-plugin-vue": "^10.0.0",
58
87
  "eslint-plugin-yml": "^1.17.0",
59
88
  "globals": "^16.0.0",
60
89
  "typescript-eslint": "^8.29.1"
package/react.js ADDED
@@ -0,0 +1,66 @@
1
+ // @ts-check
2
+
3
+ import react_ from '@eslint-react/eslint-plugin'
4
+ import { tryRequirePkg } from '@pkgr/utils'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ import { isTsAvailable, prettierExtends } from './_util.js'
8
+
9
+ export const reactJsx = tseslint.config({
10
+ name: '@1stg/react-jsx',
11
+ extends: [react_.configs.recommended, prettierExtends],
12
+ settings: {
13
+ react: {
14
+ version: 'detect',
15
+ },
16
+ },
17
+ })
18
+
19
+ const reactHooks_ = tryRequirePkg('eslint-plugin-react-hooks')
20
+
21
+ export const react = tseslint.config(
22
+ {
23
+ name: '@1stg/react',
24
+ files: ['**/*.{js,jsx,tsx}'],
25
+ extends: [reactJsx],
26
+ rules: {
27
+ '@eslint-react/avoid-shorthand-boolean': 2,
28
+ // '@eslint-react/naming-convention/handler-prop': [
29
+ // 2,
30
+ // {
31
+ // eventHandlerPrefix: false,
32
+ // eventHandlerPropPrefix: 'on',
33
+ // },
34
+ // ],
35
+ 'sonarjs/function-name': isTsAvailable
36
+ ? [2, { format: String.raw`^_?[a-zA-Z][a-zA-Z0-9]*\$?$` }]
37
+ : 0,
38
+ },
39
+ },
40
+ {
41
+ name: '@1stg/react-tsx',
42
+ files: ['**/*.tsx'],
43
+ rules: {
44
+ '@eslint-react/display-name': 0,
45
+ },
46
+ },
47
+ reactHooks_
48
+ ? {
49
+ name: '@1stg/react-hooks',
50
+ files: ['**/*.{js,jsx,ts,tsx}'],
51
+ plugins: { 'react-hooks': reactHooks_ },
52
+ rules: {
53
+ 'react-hooks/rules-of-hooks': 2,
54
+ 'react-hooks/exhaustive-deps': 2,
55
+ },
56
+ }
57
+ : [],
58
+ {
59
+ name: '@1stg/react-ts',
60
+ files: ['**/*.{ts,tsx}'],
61
+ rules: {
62
+ 'no-restricted-imports': [2, 'prop-types'],
63
+ '@eslint-react/prop-types': 0,
64
+ },
65
+ },
66
+ )
package/test.js ADDED
@@ -0,0 +1,14 @@
1
+ // @ts-check
2
+
3
+ /** @import {TSESLint} from '@typescript-eslint/utils' */
4
+
5
+ import { nonSourceRules } from './_util.js'
6
+
7
+ /** @satisfies {TSESLint.FlatConfig.Config} */
8
+ export const test = {
9
+ name: '@1stg/test',
10
+ files: ['**/{__test__,test,tests}/**/*'],
11
+ rules: nonSourceRules,
12
+ }
13
+
14
+ export const tests = test
package/ts-base.js ADDED
@@ -0,0 +1,138 @@
1
+ // @ts-check
2
+
3
+ import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
4
+ import importX from 'eslint-plugin-import-x'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ import { prettierExtends, project } from './_util.js'
8
+
9
+ export const resolveSettings = {
10
+ 'import-x/external-module-folders': [
11
+ 'node_modules',
12
+ 'node_modules/@d-ts',
13
+ 'node_modules/@types',
14
+ ],
15
+ 'import-x/resolver-next': [
16
+ createTypeScriptImportResolver({
17
+ alwaysTryTypes: true,
18
+ project,
19
+ }),
20
+ ],
21
+ }
22
+
23
+ export const tsBase = tseslint.config({
24
+ name: '@1stg/ts-base',
25
+ files: ['**/*.{cts,mts,ts,tsx}'],
26
+ languageOptions: {
27
+ parserOptions: {
28
+ project,
29
+ },
30
+ },
31
+ extends: [
32
+ tseslint.configs.eslintRecommended,
33
+ tseslint.configs.recommended,
34
+ importX.flatConfigs.typescript,
35
+ prettierExtends,
36
+ ],
37
+ settings: resolveSettings,
38
+ rules: {
39
+ '@typescript-eslint/adjacent-overload-signatures': 2,
40
+ '@typescript-eslint/array-type': [
41
+ 2,
42
+ {
43
+ default: 'array-simple',
44
+ },
45
+ ],
46
+ '@typescript-eslint/ban-ts-comment': 0,
47
+ '@typescript-eslint/consistent-type-definitions': [2, 'interface'],
48
+ '@typescript-eslint/explicit-function-return-type': 0,
49
+ '@typescript-eslint/explicit-member-accessibility': [
50
+ 2,
51
+ {
52
+ accessibility: 'no-public',
53
+ overrides: {
54
+ parameterProperties: 'off',
55
+ },
56
+ },
57
+ ],
58
+ '@typescript-eslint/explicit-module-boundary-types': 0,
59
+ '@typescript-eslint/member-ordering': 2,
60
+ // eslint-disable-line sonarjs/todo-tag -- TODO: find better config
61
+ '@typescript-eslint/naming-convention': 0,
62
+ '@typescript-eslint/no-empty-function': 2,
63
+ '@typescript-eslint/no-extraneous-class': [
64
+ 2,
65
+ {
66
+ allowWithDecorator: true,
67
+ },
68
+ ],
69
+ '@typescript-eslint/no-for-in-array': 2,
70
+ '@typescript-eslint/no-non-null-assertion': 0,
71
+ '@typescript-eslint/no-parameter-properties': 0,
72
+ '@typescript-eslint/no-require-imports': 2,
73
+ '@typescript-eslint/no-this-alias': [
74
+ 2,
75
+ {
76
+ allowDestructuring: true,
77
+ allowedNames: ['self'],
78
+ },
79
+ ],
80
+ '@typescript-eslint/no-use-before-define': [
81
+ 2,
82
+ {
83
+ classes: false,
84
+ functions: false,
85
+ },
86
+ ],
87
+ '@typescript-eslint/no-useless-constructor': 2,
88
+ '@typescript-eslint/no-unused-vars': [
89
+ 2,
90
+ {
91
+ argsIgnorePattern: '^_',
92
+ varsIgnorePattern: '^_',
93
+ },
94
+ ],
95
+ '@typescript-eslint/prefer-for-of': 2,
96
+ '@typescript-eslint/prefer-function-type': 2,
97
+ '@typescript-eslint/prefer-ts-expect-error': 2,
98
+ '@typescript-eslint/sort-type-constituents': 2,
99
+ '@typescript-eslint/triple-slash-reference': [
100
+ 2,
101
+ {
102
+ lib: 'never',
103
+ path: 'always',
104
+ types: 'prefer-import',
105
+ },
106
+ ],
107
+ '@typescript-eslint/unified-signatures': 2,
108
+ camelcase: 0,
109
+ 'import-x/default': 0,
110
+ 'import-x/named': 0,
111
+ 'import-x/namespace': 0,
112
+ 'import-x/no-duplicates': 2,
113
+ 'import-x/no-named-as-default': 0,
114
+ 'import-x/no-named-as-default-member': 0,
115
+ 'no-empty-function': 0,
116
+ 'no-restricted-syntax': [
117
+ 2,
118
+ {
119
+ selector:
120
+ 'TSTypeAliasDeclaration > .typeAnnotation[type=/^(TS.*Keyword|TSTypeReference)$/]:not(:has(TSTypeParameterInstantiation)):not(:has(TSQualifiedName))',
121
+ message:
122
+ 'Type alias references simple `Ts*Keyword`s or `TSTypeReference` only is not allowed',
123
+ },
124
+ ],
125
+ 'no-unused-vars': 0,
126
+ 'no-use-before-define': 0,
127
+ 'no-useless-constructor': 0,
128
+ // covered by @typescript-eslint/no-floating-promises
129
+ 'promise/always-return': 0,
130
+ 'promise/catch-or-return': 0,
131
+ // ts itself has guaranteed it
132
+ 'unicorn/no-array-callback-reference': 0,
133
+ // covered by @typescript-eslint/no-extraneous-class
134
+ 'unicorn/no-static-only-class': 0,
135
+ // covered by @typescript-eslint/no-this-alias
136
+ 'unicorn/no-this-assignment': 0,
137
+ },
138
+ })
package/vitest.js ADDED
@@ -0,0 +1,17 @@
1
+ // @ts-check
2
+
3
+ import vitest_ from '@vitest/eslint-plugin'
4
+ import globals from 'globals'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ import { test } from './test.js'
8
+
9
+ export const vitest = tseslint.config({
10
+ name: '@1stg/vitest',
11
+ files: ['**/*.{spec,test}.{js,jsx,ts,tsx}'],
12
+ extends: [vitest_.configs.recommended],
13
+ languageOptions: {
14
+ globals: globals.vitest,
15
+ },
16
+ rules: test.rules,
17
+ })
package/vue.js ADDED
@@ -0,0 +1,57 @@
1
+ // @ts-check
2
+
3
+ import babelParser from '@babel/eslint-parser'
4
+ import * as markup from 'eslint-plugin-markup'
5
+ import vue_ from 'eslint-plugin-vue'
6
+ import tseslint from 'typescript-eslint'
7
+ import vueParser from 'vue-eslint-parser'
8
+
9
+ import { isTsAvailable, prettierExtends } from './_util.js'
10
+ import { jsBase } from './js-base.js'
11
+ import { tsBase } from './ts-base.js'
12
+
13
+ const vueExtends = tseslint.config(
14
+ vue_.configs['flat/recommended'],
15
+ prettierExtends,
16
+ )
17
+
18
+ export const vue = tseslint.config(
19
+ {
20
+ ...jsBase,
21
+ name: '@1stg/js-vue',
22
+ files: /** @type {string[]} */ (
23
+ [...jsBase.files, !isTsAvailable && '**/*.vue'].filter(Boolean)
24
+ ),
25
+ languageOptions: {
26
+ parser: vueParser,
27
+ parserOptions: {
28
+ ...jsBase.languageOptions.parserOptions,
29
+ parser: babelParser,
30
+ },
31
+ },
32
+ extends: vueExtends,
33
+ },
34
+ isTsAvailable
35
+ ? {
36
+ name: '@1stg/ts-vue',
37
+ extends: [...tsBase, ...vueExtends],
38
+ files: [.../** @type {string[]} */ (tsBase[0].files), '**/*.vue'],
39
+ languageOptions: {
40
+ parser: vueParser,
41
+ parserOptions: {
42
+ parser: tseslint.parser,
43
+ extraFileExtensions: ['.vue'],
44
+ },
45
+ },
46
+ }
47
+ : [],
48
+ {
49
+ name: '@1stg/vue',
50
+ files: ['**/*.vue'],
51
+ extends: [markup.configs.flatRecommended, ...vueExtends],
52
+ rules: {
53
+ 'no-unused-vars': 0,
54
+ '@typescript-eslint/no-unused-vars': 0,
55
+ },
56
+ },
57
+ )