@dmitryrechkin/eslint-standard 1.3.5 → 1.3.7

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/eslint.config.mjs CHANGED
@@ -8,6 +8,7 @@ import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
8
8
  import perfectionistPlugin from 'eslint-plugin-perfectionist';
9
9
  import jsdocIndentPlugin from './src/plugins/jsdoc-indent.mjs';
10
10
  import interfaceBracePlugin from './src/plugins/interface-brace.mjs';
11
+ import switchCaseBracePlugin from './src/plugins/switch-case-brace.mjs';
11
12
  import securityPlugin from 'eslint-plugin-security';
12
13
  import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
13
14
  import promisePlugin from 'eslint-plugin-promise';
@@ -48,6 +49,7 @@ export default function ({
48
49
  'perfectionist': perfectionistPlugin,
49
50
  'jsdoc-indent': jsdocIndentPlugin,
50
51
  'interface-brace': interfaceBracePlugin,
52
+ 'switch-case-brace': switchCaseBracePlugin,
51
53
  'security': securityPlugin,
52
54
  'jsx-a11y': jsxA11yPlugin,
53
55
  'promise': promisePlugin,
@@ -182,6 +184,9 @@ export default function ({
182
184
 
183
185
  // Enhanced: Interface brace style
184
186
  'interface-brace/interface-brace-style': 'error',
187
+
188
+ // Enhanced: Switch case brace style - Allman style for case blocks
189
+ 'switch-case-brace/switch-case-brace-style': 'error',
185
190
 
186
191
  // Additional naming conventions based on coding standards
187
192
  '@typescript-eslint/naming-convention': [
@@ -795,6 +800,7 @@ export default function ({
795
800
  // Test file specific overrides
796
801
  {
797
802
  files: ['**/*.test.{js,jsx,ts,tsx}', '**/*.spec.{js,jsx,ts,tsx}', '**/tests/**/*.{js,jsx,ts,tsx}'],
803
+ ignores: ['**/tests/fixtures/**/*'], // Don't apply test overrides to fixture files
798
804
  rules: {
799
805
  // Disable function scoping rule for test helpers
800
806
  'unicorn/consistent-function-scoping': 'off',
@@ -808,9 +814,16 @@ export default function ({
808
814
  exceptions: ['id', 'fn']
809
815
  }],
810
816
 
811
- // Relax rules that are too strict for tests
812
- 'unicorn/no-null': 'warn', // APIs often legitimately use null
813
- 'functional/no-loop-statements': 'warn', // Loops can be clearer than functional alternatives in tests
817
+ // Disable rules inappropriate for test files
818
+ 'functional/no-loop-statements': 'off', // Loops are often clearer than functional alternatives in tests
819
+ 'functional/no-let': 'off', // Test setup requires mutable variables
820
+ 'functional/immutable-data': 'off', // Test mocking requires object mutations
821
+ 'security/detect-non-literal-fs-filename': 'off', // Tests legitimately need dynamic file paths
822
+ 'unicorn/prefer-module': 'off', // Tests may need __dirname for reliable paths
823
+
824
+ // Keep as warnings - still worth improving when possible
825
+ 'unicorn/no-null': 'off', // APIs often use null, but const can be better
826
+ 'sonarjs/no-duplicate-string': 'off', // Test strings repeat, but constants still help readability
814
827
  },
815
828
  },
816
829
  ];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dmitryrechkin/eslint-standard",
3
3
  "description": "This package provides a shared ESLint configuration which includes TypeScript support and a set of specific linting rules designed to ensure high-quality and consistent code style across projects.",
4
- "version": "1.3.5",
4
+ "version": "1.3.7",
5
5
  "main": "eslint.config.mjs",
6
6
  "bin": {
7
7
  "eslint-standard": "./src/cli/index.mjs"
@@ -24,6 +24,7 @@
24
24
  "test:security": "node tests/test-security-rules.js",
25
25
  "test:sonarjs": "node tests/test-sonarjs-rules.js",
26
26
  "test:unicorn": "node tests/test-unicorn-rules.js",
27
+ "test:switch-case": "node tests/test-switch-case-simple.js",
27
28
  "test:cli": "node tests/test-cli.js",
28
29
  "test:install": "node tests/test-install-simulation.js"
29
30
  },
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Custom ESLint plugin rule to enforce Allman-style brace placement for switch case blocks
3
+ * Forces case block opening braces to be on new lines
4
+ *
5
+ * Example:
6
+ * case 'VALUE':
7
+ * {
8
+ * // code here
9
+ * }
10
+ */
11
+
12
+ const switchCaseBraceRule = {
13
+ meta: {
14
+ type: 'layout',
15
+ docs: {
16
+ description: 'Enforce Allman-style brace placement for switch case blocks',
17
+ category: 'Stylistic Issues',
18
+ recommended: false
19
+ },
20
+ fixable: 'whitespace',
21
+ schema: [],
22
+ messages: {
23
+ expectedNewlineBeforeOpeningBrace: 'Expected a newline before opening brace in case block.',
24
+ expectedNewlineAfterOpeningBrace: 'Expected a newline after opening brace in case block.'
25
+ }
26
+ },
27
+
28
+ create(context) {
29
+ const sourceCode = context.getSourceCode();
30
+
31
+ /**
32
+ * Check if a case clause has a block statement and enforce proper brace placement
33
+ * @param {ASTNode} node - The SwitchCase node
34
+ */
35
+ function checkCaseClause(node) {
36
+ // Only process case clauses that have a single BlockStatement
37
+ if (node.consequent.length !== 1 || node.consequent[0].type !== 'BlockStatement') {
38
+ return;
39
+ }
40
+
41
+ const blockStatement = node.consequent[0];
42
+ const openingBrace = sourceCode.getFirstToken(blockStatement);
43
+ const closingBrace = sourceCode.getLastToken(blockStatement);
44
+
45
+ // Get the colon token after case label
46
+ const colonToken = sourceCode.getTokenAfter(node.test || sourceCode.getFirstToken(node));
47
+
48
+ // Check if opening brace is on the same line as the colon
49
+ if (colonToken.loc.end.line === openingBrace.loc.start.line) {
50
+ context.report({
51
+ node: blockStatement,
52
+ messageId: 'expectedNewlineBeforeOpeningBrace',
53
+ fix(fixer) {
54
+ // Add newline and proper indentation before opening brace
55
+ const indentation = '\t'.repeat(getIndentLevel(node) + 1);
56
+ return fixer.replaceTextRange(
57
+ [colonToken.range[1], openingBrace.range[0]],
58
+ `\n${indentation}`
59
+ );
60
+ }
61
+ });
62
+ }
63
+
64
+ // Check if content inside braces needs proper formatting
65
+ if (blockStatement.body.length > 0) {
66
+ const firstStatement = blockStatement.body[0];
67
+ const firstStatementToken = sourceCode.getFirstToken(firstStatement);
68
+
69
+ // Check if the first statement is on the same line as opening brace
70
+ if (openingBrace.loc.end.line === firstStatementToken.loc.start.line) {
71
+ context.report({
72
+ node: firstStatement,
73
+ messageId: 'expectedNewlineAfterOpeningBrace',
74
+ fix(fixer) {
75
+ // Add newline and proper indentation after opening brace
76
+ const indentation = '\t'.repeat(getIndentLevel(node) + 2);
77
+ const fixes = [];
78
+
79
+ // Add newline after opening brace
80
+ fixes.push(fixer.insertTextAfter(openingBrace, `\n${indentation}`));
81
+
82
+ // Also ensure closing brace is on its own line with proper indentation
83
+ const lastStatement = blockStatement.body[blockStatement.body.length - 1];
84
+ const lastToken = sourceCode.getLastToken(lastStatement);
85
+ const closingBraceIndentation = '\t'.repeat(getIndentLevel(node) + 1);
86
+
87
+ if (lastToken.loc.end.line === closingBrace.loc.start.line) {
88
+ fixes.push(fixer.insertTextBefore(closingBrace, `\n${closingBraceIndentation}`));
89
+ }
90
+
91
+ return fixes;
92
+ }
93
+ });
94
+ }
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Get the indentation level for a node
100
+ * @param {ASTNode} node - The node to check
101
+ * @returns {number} The indentation level
102
+ */
103
+ function getIndentLevel(node) {
104
+ const line = sourceCode.lines[node.loc.start.line - 1];
105
+ const match = line.match(/^(\t*)/);
106
+ return match ? match[1].length : 0;
107
+ }
108
+
109
+ return {
110
+ SwitchCase: checkCaseClause
111
+ };
112
+ }
113
+ };
114
+
115
+ export default {
116
+ rules: {
117
+ 'switch-case-brace-style': switchCaseBraceRule
118
+ }
119
+ };