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