eslint-plugin-code-style 1.7.1 → 1.7.2
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 +12 -2
- package/index.js +246 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,14 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.7.2] - 2026-02-02
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`enum-format`** - Fix double comma bug when auto-fixing trailing comma and closing brace position; check for comma token after member, not just member text
|
|
15
|
+
- **`interface-format`** - Same fix as enum-format for trailing comma detection
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
10
19
|
## [1.7.1] - 2026-02-02
|
|
11
20
|
|
|
12
21
|
### Fixed
|
|
13
22
|
|
|
14
23
|
- **`no-empty-lines-in-function-params`** - Detect empty lines between destructured properties inside ObjectPattern params
|
|
15
24
|
- **`component-props-inline-type`** - Handle TSIntersectionType (e.g., `ButtonHTMLAttributes & { prop: Type }`): check `&` position, opening brace position, and apply formatting rules to type literals within intersections
|
|
16
|
-
- **`enum-type-enforcement`** -
|
|
17
|
-
- **`ternary-condition-multiline`** - Improve simple ternary prefix calculation
|
|
25
|
+
- **`enum-type-enforcement`** - Handle TSIntersectionType to track typed props; fix extractTypeInfoHandler argument for TSPropertySignature members
|
|
26
|
+
- **`ternary-condition-multiline`** - Improve simple ternary prefix calculation for object properties; add checks for `?` on same line as condition end and empty lines before `?` or `:`; fix multiline formatting to not add leading newline
|
|
18
27
|
|
|
19
28
|
---
|
|
20
29
|
|
|
@@ -1075,6 +1084,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1075
1084
|
|
|
1076
1085
|
---
|
|
1077
1086
|
|
|
1087
|
+
[1.7.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.1...v1.7.2
|
|
1078
1088
|
[1.7.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.0...v1.7.1
|
|
1079
1089
|
[1.7.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.6.6...v1.7.0
|
|
1080
1090
|
[1.6.6]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.6.5...v1.6.6
|
package/index.js
CHANGED
|
@@ -4049,7 +4049,7 @@ const ternaryConditionMultiline = {
|
|
|
4049
4049
|
const singleLineText = getTernarySingleLineHandler(node);
|
|
4050
4050
|
const indent = getLineIndentHandler(node);
|
|
4051
4051
|
|
|
4052
|
-
// Check if the parent needs prefix text (like "const x = ")
|
|
4052
|
+
// Check if the parent needs prefix text (like "const x = " or "key: ")
|
|
4053
4053
|
let prefixLength = 0;
|
|
4054
4054
|
const parent = node.parent;
|
|
4055
4055
|
|
|
@@ -4066,6 +4066,11 @@ const ternaryConditionMultiline = {
|
|
|
4066
4066
|
const leftText = sourceCode.getText(parent.left);
|
|
4067
4067
|
|
|
4068
4068
|
prefixLength = leftText.length + 3; // left + " = "
|
|
4069
|
+
} else if (parent && parent.type === "Property" && parent.value === node) {
|
|
4070
|
+
// Object property: key: ternary
|
|
4071
|
+
const keyText = sourceCode.getText(parent.key);
|
|
4072
|
+
|
|
4073
|
+
prefixLength = keyText.length + 2; // key + ": "
|
|
4069
4074
|
}
|
|
4070
4075
|
|
|
4071
4076
|
// Check if single line would fit
|
|
@@ -4155,6 +4160,24 @@ const ternaryConditionMultiline = {
|
|
|
4155
4160
|
isCorrectionNeeded = true;
|
|
4156
4161
|
}
|
|
4157
4162
|
}
|
|
4163
|
+
|
|
4164
|
+
// Check for empty lines before ? (between condition and ?)
|
|
4165
|
+
if (!isCorrectionNeeded) {
|
|
4166
|
+
const questionToken = sourceCode.getTokenAfter(test, (t) => t.value === "?");
|
|
4167
|
+
|
|
4168
|
+
if (questionToken && questionToken.loc.start.line > test.loc.end.line + 1) {
|
|
4169
|
+
isCorrectionNeeded = true;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4173
|
+
// Check for empty lines before : (between consequent and :)
|
|
4174
|
+
if (!isCorrectionNeeded) {
|
|
4175
|
+
const colonToken = sourceCode.getTokenAfter(node.consequent, (t) => t.value === ":");
|
|
4176
|
+
|
|
4177
|
+
if (colonToken && colonToken.loc.start.line > node.consequent.loc.end.line + 1) {
|
|
4178
|
+
isCorrectionNeeded = true;
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4158
4181
|
}
|
|
4159
4182
|
|
|
4160
4183
|
if (isCorrectionNeeded) {
|
|
@@ -4164,7 +4187,6 @@ const ternaryConditionMultiline = {
|
|
|
4164
4187
|
const lineText = sourceCode.lines[node.loc.start.line - 1];
|
|
4165
4188
|
const baseIndent = lineText.match(/^\s*/)[0];
|
|
4166
4189
|
const conditionIndent = baseIndent + " ";
|
|
4167
|
-
const branchIndent = baseIndent + " ";
|
|
4168
4190
|
|
|
4169
4191
|
const buildMultilineHandler = (n) => {
|
|
4170
4192
|
if (n.type === "LogicalExpression" && !isParenthesizedHandler(n)) {
|
|
@@ -4180,7 +4202,9 @@ const ternaryConditionMultiline = {
|
|
|
4180
4202
|
const consequentText = sourceCode.getText(node.consequent);
|
|
4181
4203
|
const alternateText = sourceCode.getText(node.alternate);
|
|
4182
4204
|
|
|
4183
|
-
|
|
4205
|
+
// No leading newline - keep first operand on same line as whatever precedes it
|
|
4206
|
+
// Use conditionIndent for ? and : to align with || operators
|
|
4207
|
+
const newText = `${buildMultilineHandler(test)}\n${conditionIndent}? ${consequentText}\n${conditionIndent}: ${alternateText}`;
|
|
4184
4208
|
|
|
4185
4209
|
return fixer.replaceText(node, newText);
|
|
4186
4210
|
},
|
|
@@ -4481,6 +4505,20 @@ const enumTypeEnforcement = {
|
|
|
4481
4505
|
return null;
|
|
4482
4506
|
};
|
|
4483
4507
|
|
|
4508
|
+
// Helper to process TSTypeLiteral members
|
|
4509
|
+
const processTypeLiteralMembersHandler = (members) => {
|
|
4510
|
+
members.forEach((member) => {
|
|
4511
|
+
if (member.type === "TSPropertySignature" && member.key?.type === "Identifier") {
|
|
4512
|
+
const propName = member.key.name;
|
|
4513
|
+
const typeInfo = extractTypeInfoHandler(member.typeAnnotation);
|
|
4514
|
+
|
|
4515
|
+
if (typeInfo) {
|
|
4516
|
+
typeAnnotatedVars.set(propName, typeInfo);
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4519
|
+
});
|
|
4520
|
+
};
|
|
4521
|
+
|
|
4484
4522
|
// Track type-annotated parameters in function/component definitions
|
|
4485
4523
|
const trackTypedParamsHandler = (params) => {
|
|
4486
4524
|
params.forEach((param) => {
|
|
@@ -4489,14 +4527,14 @@ const enumTypeEnforcement = {
|
|
|
4489
4527
|
const annotation = param.typeAnnotation.typeAnnotation;
|
|
4490
4528
|
|
|
4491
4529
|
if (annotation && annotation.type === "TSTypeLiteral") {
|
|
4492
|
-
annotation.members
|
|
4493
|
-
|
|
4494
|
-
const propName = member.key.name;
|
|
4495
|
-
const typeInfo = extractTypeInfoHandler(member.typeAnnotation);
|
|
4530
|
+
processTypeLiteralMembersHandler(annotation.members);
|
|
4531
|
+
}
|
|
4496
4532
|
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4533
|
+
// Handle intersection types: ButtonHTMLAttributes<HTMLButtonElement> & { variant?: ButtonVariantType }
|
|
4534
|
+
if (annotation && annotation.type === "TSIntersectionType") {
|
|
4535
|
+
annotation.types.forEach((intersectionType) => {
|
|
4536
|
+
if (intersectionType.type === "TSTypeLiteral") {
|
|
4537
|
+
processTypeLiteralMembersHandler(intersectionType.members);
|
|
4500
4538
|
}
|
|
4501
4539
|
});
|
|
4502
4540
|
}
|
|
@@ -14412,6 +14450,36 @@ const componentPropsInlineType = {
|
|
|
14412
14450
|
}
|
|
14413
14451
|
}
|
|
14414
14452
|
});
|
|
14453
|
+
|
|
14454
|
+
// Check that last member has trailing comma
|
|
14455
|
+
if (members.length > 0) {
|
|
14456
|
+
const lastMember = members[members.length - 1];
|
|
14457
|
+
const lastMemberText = sourceCode.getText(lastMember);
|
|
14458
|
+
|
|
14459
|
+
if (!lastMemberText.trimEnd().endsWith(",")) {
|
|
14460
|
+
context.report({
|
|
14461
|
+
fix: (fixer) => fixer.insertTextAfter(lastMember, ","),
|
|
14462
|
+
message: "Last props type property must have trailing comma",
|
|
14463
|
+
node: lastMember,
|
|
14464
|
+
});
|
|
14465
|
+
}
|
|
14466
|
+
}
|
|
14467
|
+
|
|
14468
|
+
// Check for empty lines before closing brace
|
|
14469
|
+
if (members.length > 0 && closeBraceToken) {
|
|
14470
|
+
const lastMember = members[members.length - 1];
|
|
14471
|
+
|
|
14472
|
+
if (closeBraceToken.loc.start.line - lastMember.loc.end.line > 1) {
|
|
14473
|
+
context.report({
|
|
14474
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
14475
|
+
[lastMember.range[1], closeBraceToken.range[0]],
|
|
14476
|
+
"\n" + baseIndent,
|
|
14477
|
+
),
|
|
14478
|
+
message: "No empty line before closing brace in props type",
|
|
14479
|
+
node: closeBraceToken,
|
|
14480
|
+
});
|
|
14481
|
+
}
|
|
14482
|
+
}
|
|
14415
14483
|
}
|
|
14416
14484
|
|
|
14417
14485
|
return;
|
|
@@ -14619,6 +14687,20 @@ const componentPropsInlineType = {
|
|
|
14619
14687
|
}
|
|
14620
14688
|
}
|
|
14621
14689
|
});
|
|
14690
|
+
|
|
14691
|
+
// Check that last member has trailing comma
|
|
14692
|
+
if (members.length > 0) {
|
|
14693
|
+
const lastMember = members[members.length - 1];
|
|
14694
|
+
const lastMemberText = sourceCode.getText(lastMember);
|
|
14695
|
+
|
|
14696
|
+
if (!lastMemberText.trimEnd().endsWith(",")) {
|
|
14697
|
+
context.report({
|
|
14698
|
+
fix: (fixer) => fixer.insertTextAfter(lastMember, ","),
|
|
14699
|
+
message: "Last props type property must have trailing comma",
|
|
14700
|
+
node: lastMember,
|
|
14701
|
+
});
|
|
14702
|
+
}
|
|
14703
|
+
}
|
|
14622
14704
|
}
|
|
14623
14705
|
};
|
|
14624
14706
|
|
|
@@ -16956,19 +17038,24 @@ const enumFormat = {
|
|
|
16956
17038
|
}
|
|
16957
17039
|
|
|
16958
17040
|
// Check member ends with comma, not semicolon
|
|
16959
|
-
|
|
17041
|
+
// Skip last member when multiple members - handled by combined check below
|
|
17042
|
+
const isLastMember = index === members.length - 1;
|
|
16960
17043
|
|
|
16961
|
-
if (
|
|
16962
|
-
|
|
16963
|
-
fix(fixer) {
|
|
16964
|
-
const lastChar = memberText.lastIndexOf(";");
|
|
16965
|
-
const absolutePos = member.range[0] + lastChar;
|
|
17044
|
+
if (!isLastMember || members.length === 1) {
|
|
17045
|
+
const memberText = sourceCode.getText(member);
|
|
16966
17046
|
|
|
16967
|
-
|
|
16968
|
-
|
|
16969
|
-
|
|
16970
|
-
|
|
16971
|
-
|
|
17047
|
+
if (memberText.trimEnd().endsWith(";")) {
|
|
17048
|
+
context.report({
|
|
17049
|
+
fix(fixer) {
|
|
17050
|
+
const lastChar = memberText.lastIndexOf(";");
|
|
17051
|
+
const absolutePos = member.range[0] + lastChar;
|
|
17052
|
+
|
|
17053
|
+
return fixer.replaceTextRange([absolutePos, absolutePos + 1], ",");
|
|
17054
|
+
},
|
|
17055
|
+
message: "Enum members must end with comma (,) not semicolon (;)",
|
|
17056
|
+
node: member,
|
|
17057
|
+
});
|
|
17058
|
+
}
|
|
16972
17059
|
}
|
|
16973
17060
|
|
|
16974
17061
|
// Check formatting for multiple members
|
|
@@ -17018,6 +17105,67 @@ const enumFormat = {
|
|
|
17018
17105
|
}
|
|
17019
17106
|
}
|
|
17020
17107
|
});
|
|
17108
|
+
|
|
17109
|
+
// Check closing brace position and trailing comma/semicolon (for multiple members)
|
|
17110
|
+
if (members.length > 1) {
|
|
17111
|
+
const lastMemberText = sourceCode.getText(lastMember);
|
|
17112
|
+
const trimmedText = lastMemberText.trimEnd();
|
|
17113
|
+
// Check both: text ends with comma OR there's a comma token after the member
|
|
17114
|
+
const tokenAfterLast = sourceCode.getTokenAfter(lastMember);
|
|
17115
|
+
const hasTrailingComma = trimmedText.endsWith(",") || (tokenAfterLast && tokenAfterLast.value === ",");
|
|
17116
|
+
const hasTrailingSemicolon = trimmedText.endsWith(";");
|
|
17117
|
+
const braceOnSameLine = closeBraceToken && closeBraceToken.loc.start.line === lastMember.loc.end.line;
|
|
17118
|
+
|
|
17119
|
+
// Handle semicolon on last member (needs replacement with comma)
|
|
17120
|
+
if (hasTrailingSemicolon) {
|
|
17121
|
+
const lastSemicolon = lastMemberText.lastIndexOf(";");
|
|
17122
|
+
const absolutePos = lastMember.range[0] + lastSemicolon;
|
|
17123
|
+
|
|
17124
|
+
if (braceOnSameLine) {
|
|
17125
|
+
// Both semicolon and brace issues - fix together
|
|
17126
|
+
context.report({
|
|
17127
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
17128
|
+
[absolutePos, closeBraceToken.range[0]],
|
|
17129
|
+
",\n" + baseIndent,
|
|
17130
|
+
),
|
|
17131
|
+
message: "Last enum member must end with comma and closing brace must be on its own line",
|
|
17132
|
+
node: lastMember,
|
|
17133
|
+
});
|
|
17134
|
+
} else {
|
|
17135
|
+
// Just semicolon issue
|
|
17136
|
+
context.report({
|
|
17137
|
+
fix: (fixer) => fixer.replaceTextRange([absolutePos, absolutePos + 1], ","),
|
|
17138
|
+
message: "Enum members must end with comma (,) not semicolon (;)",
|
|
17139
|
+
node: lastMember,
|
|
17140
|
+
});
|
|
17141
|
+
}
|
|
17142
|
+
} else if (!hasTrailingComma && braceOnSameLine) {
|
|
17143
|
+
// Both missing comma and brace issues - fix together
|
|
17144
|
+
context.report({
|
|
17145
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
17146
|
+
[lastMember.range[1], closeBraceToken.range[0]],
|
|
17147
|
+
",\n" + baseIndent,
|
|
17148
|
+
),
|
|
17149
|
+
message: "Last enum member must have trailing comma and closing brace must be on its own line",
|
|
17150
|
+
node: lastMember,
|
|
17151
|
+
});
|
|
17152
|
+
} else if (!hasTrailingComma) {
|
|
17153
|
+
context.report({
|
|
17154
|
+
fix: (fixer) => fixer.insertTextAfter(lastMember, ","),
|
|
17155
|
+
message: "Last enum member must have trailing comma",
|
|
17156
|
+
node: lastMember,
|
|
17157
|
+
});
|
|
17158
|
+
} else if (braceOnSameLine) {
|
|
17159
|
+
context.report({
|
|
17160
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
17161
|
+
[lastMember.range[1], closeBraceToken.range[0]],
|
|
17162
|
+
"\n" + baseIndent,
|
|
17163
|
+
),
|
|
17164
|
+
message: "Closing brace must be on its own line",
|
|
17165
|
+
node: closeBraceToken,
|
|
17166
|
+
});
|
|
17167
|
+
}
|
|
17168
|
+
}
|
|
17021
17169
|
},
|
|
17022
17170
|
};
|
|
17023
17171
|
},
|
|
@@ -17201,19 +17349,24 @@ const interfaceFormat = {
|
|
|
17201
17349
|
}
|
|
17202
17350
|
|
|
17203
17351
|
// Check property ends with comma, not semicolon
|
|
17204
|
-
|
|
17352
|
+
// Skip last member when multiple members - handled by combined check below
|
|
17353
|
+
const isLastMember = index === members.length - 1;
|
|
17205
17354
|
|
|
17206
|
-
if (
|
|
17207
|
-
|
|
17208
|
-
fix(fixer) {
|
|
17209
|
-
const lastChar = memberText.lastIndexOf(";");
|
|
17210
|
-
const absolutePos = member.range[0] + lastChar;
|
|
17355
|
+
if (!isLastMember || members.length === 1) {
|
|
17356
|
+
const memberText = sourceCode.getText(member);
|
|
17211
17357
|
|
|
17212
|
-
|
|
17213
|
-
|
|
17214
|
-
|
|
17215
|
-
|
|
17216
|
-
|
|
17358
|
+
if (memberText.trimEnd().endsWith(";")) {
|
|
17359
|
+
context.report({
|
|
17360
|
+
fix(fixer) {
|
|
17361
|
+
const lastChar = memberText.lastIndexOf(";");
|
|
17362
|
+
const absolutePos = member.range[0] + lastChar;
|
|
17363
|
+
|
|
17364
|
+
return fixer.replaceTextRange([absolutePos, absolutePos + 1], ",");
|
|
17365
|
+
},
|
|
17366
|
+
message: "Interface properties must end with comma (,) not semicolon (;)",
|
|
17367
|
+
node: member,
|
|
17368
|
+
});
|
|
17369
|
+
}
|
|
17217
17370
|
}
|
|
17218
17371
|
|
|
17219
17372
|
// Check formatting for multiple members
|
|
@@ -17263,6 +17416,67 @@ const interfaceFormat = {
|
|
|
17263
17416
|
}
|
|
17264
17417
|
}
|
|
17265
17418
|
});
|
|
17419
|
+
|
|
17420
|
+
// Check closing brace position and trailing comma/semicolon (for multiple members)
|
|
17421
|
+
if (members.length > 1) {
|
|
17422
|
+
const lastMemberText = sourceCode.getText(lastMember);
|
|
17423
|
+
const trimmedText = lastMemberText.trimEnd();
|
|
17424
|
+
// Check both: text ends with comma OR there's a comma token after the member
|
|
17425
|
+
const tokenAfterLast = sourceCode.getTokenAfter(lastMember);
|
|
17426
|
+
const hasTrailingComma = trimmedText.endsWith(",") || (tokenAfterLast && tokenAfterLast.value === ",");
|
|
17427
|
+
const hasTrailingSemicolon = trimmedText.endsWith(";");
|
|
17428
|
+
const braceOnSameLine = closeBraceToken.loc.start.line === lastMember.loc.end.line;
|
|
17429
|
+
|
|
17430
|
+
// Handle semicolon on last member (needs replacement with comma)
|
|
17431
|
+
if (hasTrailingSemicolon) {
|
|
17432
|
+
const lastSemicolon = lastMemberText.lastIndexOf(";");
|
|
17433
|
+
const absolutePos = lastMember.range[0] + lastSemicolon;
|
|
17434
|
+
|
|
17435
|
+
if (braceOnSameLine) {
|
|
17436
|
+
// Both semicolon and brace issues - fix together
|
|
17437
|
+
context.report({
|
|
17438
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
17439
|
+
[absolutePos, closeBraceToken.range[0]],
|
|
17440
|
+
",\n" + baseIndent,
|
|
17441
|
+
),
|
|
17442
|
+
message: "Last interface property must end with comma and closing brace must be on its own line",
|
|
17443
|
+
node: lastMember,
|
|
17444
|
+
});
|
|
17445
|
+
} else {
|
|
17446
|
+
// Just semicolon issue
|
|
17447
|
+
context.report({
|
|
17448
|
+
fix: (fixer) => fixer.replaceTextRange([absolutePos, absolutePos + 1], ","),
|
|
17449
|
+
message: "Interface properties must end with comma (,) not semicolon (;)",
|
|
17450
|
+
node: lastMember,
|
|
17451
|
+
});
|
|
17452
|
+
}
|
|
17453
|
+
} else if (!hasTrailingComma && braceOnSameLine) {
|
|
17454
|
+
// Both missing comma and brace issues - fix together
|
|
17455
|
+
context.report({
|
|
17456
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
17457
|
+
[lastMember.range[1], closeBraceToken.range[0]],
|
|
17458
|
+
",\n" + baseIndent,
|
|
17459
|
+
),
|
|
17460
|
+
message: "Last interface property must have trailing comma and closing brace must be on its own line",
|
|
17461
|
+
node: lastMember,
|
|
17462
|
+
});
|
|
17463
|
+
} else if (!hasTrailingComma) {
|
|
17464
|
+
context.report({
|
|
17465
|
+
fix: (fixer) => fixer.insertTextAfter(lastMember, ","),
|
|
17466
|
+
message: "Last interface property must have trailing comma",
|
|
17467
|
+
node: lastMember,
|
|
17468
|
+
});
|
|
17469
|
+
} else if (braceOnSameLine) {
|
|
17470
|
+
context.report({
|
|
17471
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
17472
|
+
[lastMember.range[1], closeBraceToken.range[0]],
|
|
17473
|
+
"\n" + baseIndent,
|
|
17474
|
+
),
|
|
17475
|
+
message: "Closing brace must be on its own line",
|
|
17476
|
+
node: closeBraceToken,
|
|
17477
|
+
});
|
|
17478
|
+
}
|
|
17479
|
+
}
|
|
17266
17480
|
},
|
|
17267
17481
|
};
|
|
17268
17482
|
},
|
package/package.json
CHANGED