@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 +16 -3
- package/package.json +2 -1
- package/src/plugins/switch-case-brace.mjs +119 -0
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
|
-
//
|
|
812
|
-
'
|
|
813
|
-
'functional/no-
|
|
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.
|
|
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
|
+
};
|