lint-rules-alvin 1.0.5 → 1.1.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.
@@ -1,9 +1,9 @@
1
1
  import eslintPluginAstro from 'eslint-plugin-astro';
2
2
 
3
3
  /**
4
- * The `ESLint` Astro config.
4
+ * The ESLint Astro config. Extends `configs['flat/base']` and configures all rules.
5
5
  *
6
- * Extends `eslint-plugin-astro`'s `flat/base` config.
6
+ * @type {import('eslint').Linter.Config}
7
7
  */
8
8
  export const astro = [
9
9
  ...eslintPluginAstro.configs['flat/base'],
@@ -1,9 +1,14 @@
1
1
  import path from 'node:path';
2
2
  import { includeIgnoreFile } from '@eslint/compat';
3
+ import eslintJs from '@eslint/js';
3
4
  import { globalIgnores } from 'eslint/config';
5
+ import globals from 'globals';
4
6
 
5
7
  /**
6
- * The `ESLint` base config. Includes base rules, ignore files and directives.
8
+ * The ESLint base config. Includes base rules, ignore files and directives,
9
+ * and the recommended eslintJs rules.
10
+ *
11
+ * @type {import('eslint').Linter.Config}
7
12
  */
8
13
  export const base = [
9
14
  includeIgnoreFile(
@@ -15,5 +20,18 @@ export const base = [
15
20
  globalIgnores([
16
21
  'eslint.config.js',
17
22
  'eslint.config.ts'
18
- ])
23
+ ]),
24
+ {
25
+ name: 'base/env',
26
+ languageOptions: {
27
+ ecmaVersion: 2020,
28
+ sourceType: 'module',
29
+ globals: {
30
+ ...globals.browser,
31
+ ...globals.nodeBuiltin,
32
+ ...globals.serviceworker
33
+ }
34
+ }
35
+ },
36
+ eslintJs.configs.recommended
19
37
  ];
@@ -3,9 +3,15 @@ import { unnamedImportsLastRule } from '../custom_rules/unnamed-imports-last.js'
3
3
  import { jsxMultilinePropNewlineRule } from '../custom_rules/jsx-multiline-prop-newline.js';
4
4
  import { jsxNoSingleObjectCurlyNewlineRule } from '../custom_rules/jsx-no-single-object-curly-newline.js';
5
5
  import { maxChainPerLineRule } from '../custom_rules/max-chain-per-line.js';
6
- import { chainFirstOnNewlineRule } from '../custom_rules/chain-first-on-newline.js';
7
6
  import { multilineParenNewlineRule } from '../custom_rules/multiline-paren-newline.js';
7
+ import { multilineArrayAccessorNewlineRule } from '../custom_rules/multiline-array-accessor-newline.js';
8
+ import { destructureNewlineRule } from '../custom_rules/destructure-newline.js';
8
9
 
10
+ /**
11
+ * Provides a config that creates a plugin and configures custom rules.
12
+ *
13
+ * @type {import('eslint').Linter.Config}
14
+ */
9
15
  export const custom = {
10
16
  plugins: {
11
17
  custom: {
@@ -15,8 +21,9 @@ export const custom = {
15
21
  'jsx-multiline-prop-newline': jsxMultilinePropNewlineRule,
16
22
  'jsx-no-single-object-curly-newline': jsxNoSingleObjectCurlyNewlineRule,
17
23
  'max-chain-per-line': maxChainPerLineRule,
18
- 'chain-first-on-newline': chainFirstOnNewlineRule,
19
- 'multiline-paren-newline': multilineParenNewlineRule
24
+ 'multiline-paren-newline': multilineParenNewlineRule,
25
+ 'multiline-array-accessor-newline': multilineArrayAccessorNewlineRule,
26
+ 'destructure-newline': destructureNewlineRule
20
27
  }
21
28
  }
22
29
  },
@@ -30,16 +37,20 @@ export const custom = {
30
37
  'custom/jsx-no-single-object-curly-newline': 'error',
31
38
  'custom/max-chain-per-line': [
32
39
  'error',
33
- { maxChain: 2 }
34
- ],
35
- 'custom/chain-first-on-newline': [
36
- 'error',
37
- 'require'
40
+ {
41
+ maxChain: 2,
42
+ enforceSingleLine: true,
43
+ checkSingleLink: true
44
+ }
38
45
  ],
39
46
  'custom/multiline-paren-newline': [
40
47
  'error',
41
48
  { singleArgument: true }
49
+ ],
50
+ 'custom/multiline-array-accessor-newline': 'error',
51
+ 'custom/destructure-newline': [
52
+ 'error',
53
+ { minItems: 2 }
42
54
  ]
43
55
  }
44
56
  };
45
-
@@ -1,7 +1,9 @@
1
1
  import { importX as eslintPluginImportX } from 'eslint-plugin-import-x';
2
2
 
3
3
  /**
4
- * The `ESLint` import config.
4
+ * The ESLint import config. Configures all rules.
5
+ *
6
+ * @type {import('eslint').Linter.Config}
5
7
  */
6
8
  export const importX = {
7
9
  name: 'eslint-plugin-import-x',
@@ -2,9 +2,11 @@ import { importX as eslintPluginImportX } from 'eslint-plugin-import-x';
2
2
  import { importX } from './index.js';
3
3
 
4
4
  /**
5
- * The `ESLint` import config with a TS resolver.
5
+ * The ESLint import config with a TS resolver.
6
+ * Extends `../importX` and `flatConfigs.typescript` config.
7
+ *
8
+ * @type {import('eslint').Linter.Config}
6
9
  *
7
- * Extends `eslint-plugin-import-x`'s `typescript` flat config.
8
10
  */
9
11
  export const importXTs = {
10
12
  ...importX,
@@ -1,20 +1,23 @@
1
1
  import eslintPluginJsonc from 'eslint-plugin-jsonc';
2
2
 
3
3
  /**
4
- * The `ESLint` JSON config. Supports JSON, JSONc an JSON5.
4
+ * The ESLint JSON config. Extends `flat/recommended-with-json`,
5
+ * `...with-jsonc` and `...with-json5` for the corresponding file types.
6
+ *
7
+ * @type {import('eslint').Linter.Config}
5
8
  */
6
9
  export const json = [
7
- { // eslint-plugin-json
10
+ {
8
11
  name: 'eslint-plugin-json',
9
12
  files: [ '**/*.{json}' ],
10
13
  ...eslintPluginJsonc.configs['flat/recommended-with-json']
11
14
  },
12
- { // eslint-plugin-jsonc
15
+ {
13
16
  name: 'eslint-plugin-jsonc',
14
17
  files: [ '**/*.{jsonc}' ],
15
18
  ...eslintPluginJsonc.configs['flat/recommended-with-jsonc']
16
19
  },
17
- { // eslint-plugin-json5
20
+ {
18
21
  name: 'eslint-plugin-json5',
19
22
  files: [ '**/*.{json5}' ],
20
23
  ...eslintPluginJsonc.configs['flat/recommended-with-json5']
@@ -1,7 +1,10 @@
1
1
  import eslintPluginMarkdown from 'eslint-plugin-markdown';
2
2
 
3
3
  /**
4
- * The `ESLint` markdown config.
4
+ * The ESLint markdown config. Extends `configs.recommended` only for `md` files.
5
+ *
6
+ * @type {import('eslint').Linter.Config}
7
+ *
5
8
  */
6
9
  export const markdown = {
7
10
  name: 'eslint-plugin-markdown',
@@ -1,7 +1,15 @@
1
1
  import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
2
2
 
3
+ /**
4
+ * The ESLint `react-hooks` config. Extends `configs.flat.recommended` only for `jsx` and `tsx` files.
5
+ *
6
+ * @type {import('eslint').Linter.Config}
7
+ */
3
8
  export const reactHooks = {
4
9
  name: 'eslint-plugin-react-hooks',
5
10
  files: [ '**/*.{jsx,tsx}' ],
6
- ...eslintPluginReactHooks.configs.flat.recommended
11
+ ...eslintPluginReactHooks
12
+ .configs
13
+ .flat
14
+ .recommended
7
15
  };
@@ -1,5 +1,11 @@
1
+ // eslint-disable-next-line import-x/no-deprecated, import-x/namespace, import-x/default
1
2
  import eslintPluginStylistic from '@stylistic/eslint-plugin';
2
3
 
4
+ /**
5
+ * The ESLint `stylistic` config. Extends `configs.recommended` and overrides all rules.
6
+ *
7
+ * @type {import('eslint').Linter.Config}
8
+ */
3
9
  export const stylistic = {
4
10
  name: 'eslint-plugin-stylistic',
5
11
  ...eslintPluginStylistic.configs.recommended,
@@ -309,6 +315,7 @@ export const stylistic = {
309
315
  'all',
310
316
  {
311
317
  ignoreJSX: 'multi-line',
318
+ nestedBinaryExpressions: false,
312
319
  ignoredNodes: [
313
320
 
314
321
  // Arrow function ternaries
@@ -1,6 +1,11 @@
1
- import eslintTs from 'typescript-eslint';
1
+ import eslintTs from '@typescript-eslint/eslint-plugin';
2
+ import eslintTsParser from '@typescript-eslint/parser';
2
3
 
3
4
  /**
5
+ * The ESLint `typescript` config. Extends `configs.base`,
6
+ * enables `languageOptions.parserOptions.projectService` and configures all rules,
7
+ * only for `ts`, `tsx`, `mts` and `cts` files.
8
+ *
4
9
  * @type {import('eslint').Linter.Config}
5
10
  */
6
11
  export const typescript = {
@@ -11,8 +16,12 @@ export const typescript = {
11
16
  '**/*.mts',
12
17
  '**/*.cts'
13
18
  ],
14
- ...eslintTs.configs.base,
15
- languageOptions: { parserOptions: { projectService: true } },
19
+ ...eslintTs.configs['flat/base'],
20
+ languageOptions: {
21
+ parser: eslintTsParser,
22
+ parserOptions: { projectService: true },
23
+ sourceType: 'module'
24
+ },
16
25
  rules: {
17
26
  '@typescript-eslint/array-type': [
18
27
  'error',
@@ -458,7 +467,6 @@ export const typescript = {
458
467
  'always'
459
468
  ],
460
469
  '@typescript-eslint/sort-type-constituents': 'off', // Deprecated in favor of sort-intersection-types, etc.
461
- '@typescript-eslint/strict-boolean-expressions': 'off',
462
470
  '@typescript-eslint/strict-boolean-expressions': [
463
471
  'error',
464
472
  {
@@ -498,7 +506,10 @@ export const typescript = {
498
506
  ignoreOverloadsWithDifferentJSDoc: true
499
507
  }
500
508
  ],
501
- '@typescript-eslint/use-unknown-in-catch-callback-variable': 'off'
509
+ '@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
510
+
511
+ // Other base ESLint overrides
512
+ 'no-undef': 'off'
502
513
 
503
514
  }
504
515
  };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @type {import('eslint').Rule.RuleModule}
3
+ */
4
+ export const destructureNewlineRule = {
5
+ meta: {
6
+ type: 'layout',
7
+ docs: {
8
+ description: 'Enforce newlines between properties in destructuring assignments',
9
+ category: 'Stylistic Issues',
10
+ recommended: false
11
+ },
12
+ fixable: 'whitespace',
13
+ schema: [
14
+ {
15
+ type: 'object',
16
+ properties: {
17
+ minItems: {
18
+ type: 'integer',
19
+ minimum: 2
20
+ }
21
+ },
22
+ additionalProperties: false
23
+ }
24
+ ],
25
+ messages: { error: 'There should be a newline between destructuring properties.' }
26
+ },
27
+
28
+ create(context) {
29
+
30
+ const sourceCode = context.sourceCode;
31
+ const { minItems = 2 } = context.options[0] || {};
32
+
33
+ return {
34
+ VariableDeclarator(node) {
35
+
36
+ if (node.id.type !== 'ObjectPattern') {
37
+
38
+ return;
39
+
40
+ }
41
+ const properties = node.id.properties;
42
+
43
+ if (properties.length < minItems) {
44
+
45
+ return;
46
+
47
+ }
48
+
49
+ for (let i = 0; i < properties.length - 1; i++) {
50
+
51
+ const currentProperty = properties[i];
52
+ const nextProperty = properties[i + 1];
53
+
54
+ const lastTokenOfCurrent = sourceCode.getLastToken(currentProperty);
55
+ const firstTokenOfNext = sourceCode.getFirstToken(nextProperty);
56
+
57
+ if (
58
+ lastTokenOfCurrent
59
+ .loc
60
+ .end
61
+ .line === firstTokenOfNext
62
+ .loc
63
+ .start
64
+ .line
65
+ ) {
66
+
67
+ context.report({
68
+ node: nextProperty,
69
+ messageId: 'error',
70
+ fix(fixer) {
71
+
72
+ return fixer.insertTextBefore(
73
+ nextProperty,
74
+ '\n'
75
+ );
76
+
77
+ }
78
+ });
79
+
80
+ }
81
+
82
+ }
83
+
84
+ }
85
+ };
86
+
87
+ }
88
+ };
@@ -20,7 +20,13 @@ export const jsxMultilinePropNewlineRule = {
20
20
 
21
21
  function isMultiline(node) {
22
22
 
23
- return node && node.loc && node.loc.start.line < node.loc.end.line;
23
+ return node && node.loc && node
24
+ .loc
25
+ .start
26
+ .line < node
27
+ .loc
28
+ .end
29
+ .line;
24
30
 
25
31
  }
26
32
 
@@ -63,7 +69,16 @@ export const jsxMultilinePropNewlineRule = {
63
69
  const firstProp = node.attributes[0];
64
70
 
65
71
  // Check if the tag name and the first prop are on the same line.
66
- if (node.name.loc.end.line === firstProp.loc.start.line) {
72
+ if (
73
+ node
74
+ .name
75
+ .loc
76
+ .end
77
+ .line === firstProp
78
+ .loc
79
+ .start
80
+ .line
81
+ ) {
67
82
 
68
83
  context.report({
69
84
  node: firstProp,
@@ -71,9 +86,12 @@ export const jsxMultilinePropNewlineRule = {
71
86
  fix(fixer) {
72
87
 
73
88
  // Get indentation of the line with the opening tag.
74
- const line = sourceCode
75
- .getLines()
76
- [node.loc.start.line - 1];
89
+ const line = sourceCode.getLines()[
90
+ node
91
+ .loc
92
+ .start
93
+ .line - 1
94
+ ];
77
95
  const baseIndentMatch = line.match(/^\s*/);
78
96
  const baseIndent = baseIndentMatch
79
97
  ? baseIndentMatch[0]
@@ -31,7 +31,15 @@ export const jsxNoSingleObjectCurlyNewlineRule = {
31
31
  }
32
32
 
33
33
  // If the expression itself isn't multiline, there are no newlines to collapse.
34
- if (expression.loc.start.line === expression.loc.end.line) {
34
+ if (
35
+ expression
36
+ .loc
37
+ .start
38
+ .line === expression
39
+ .loc
40
+ .end
41
+ .line
42
+ ) {
35
43
 
36
44
  return;
37
45
 
@@ -42,8 +50,20 @@ export const jsxNoSingleObjectCurlyNewlineRule = {
42
50
  const firstTokenInExpression = sourceCode.getFirstToken(expression);
43
51
  const lastTokenInExpression = sourceCode.getLastToken(expression);
44
52
 
45
- const hasNewlineBefore = openingBrace.loc.end.line < firstTokenInExpression.loc.start.line;
46
- const hasNewlineAfter = lastTokenInExpression.loc.end.line < closingBrace.loc.start.line;
53
+ const hasNewlineBefore = openingBrace
54
+ .loc
55
+ .end
56
+ .line < firstTokenInExpression
57
+ .loc
58
+ .start
59
+ .line;
60
+ const hasNewlineAfter = lastTokenInExpression
61
+ .loc
62
+ .end
63
+ .line < closingBrace
64
+ .loc
65
+ .start
66
+ .line;
47
67
 
48
68
  if (hasNewlineBefore || hasNewlineAfter) {
49
69