eslint-plugin-code-style 1.7.3 → 1.7.6

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +15 -10
  3. package/index.js +171 -147
  4. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.7.6] - 2026-02-02
11
+
12
+ ### Changed
13
+
14
+ - **`ternary-condition-multiline`** - Now depends only on operand count, not line length:
15
+ - ≤maxOperands (default: 3): Always collapse to single line regardless of line length
16
+ - \>maxOperands: Format multiline with each operand on its own line
17
+ - Removed `maxLineLength` option (no longer used)
18
+ - This aligns behavior with `multiline-if-conditions` rule
19
+
20
+ ---
21
+
22
+ ## [1.7.5] - 2026-02-02
23
+
24
+ ### Fixed
25
+
26
+ - **`ternary-condition-multiline`** - For ≤3 operands, always collapse to single line when `?` is on different line than condition end (enforces `condition ? value : value` format for simple ternaries)
27
+ - **`no-empty-lines-in-function-params`** - Add detection for empty lines in TSTypeLiteral (type annotation objects like `{ prop: Type }` in intersection types)
28
+
29
+ ---
30
+
31
+ ## [1.7.4] - 2026-02-02
32
+
33
+ ### Fixed
34
+
35
+ - **`no-empty-lines-in-function-params`** - Fix bug that deleted TypeScript type annotations when fixing empty lines in destructured params; now uses `getTokenAfter` instead of `getLastToken` to find closing brace
36
+ - **`no-inline-type-definitions`** - Change threshold comparison from `>` to `>=` so 2-member unions are now flagged (with default `maxUnionMembers: 2`)
37
+ - **`ternary-condition-multiline`** - Fix multiline formatting for >3 operands: first operand stays on same line as property key, `?` and `:` each on their own lines; fix circular fix bug for ≤3 operands case
38
+
39
+ ---
40
+
10
41
  ## [1.7.3] - 2026-02-02
11
42
 
12
43
  ### Fixed
@@ -1107,6 +1138,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1107
1138
 
1108
1139
  ---
1109
1140
 
1141
+ [1.7.6]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.5...v1.7.6
1142
+ [1.7.5]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.4...v1.7.5
1143
+ [1.7.4]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.3...v1.7.4
1144
+ [1.7.3]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.2...v1.7.3
1110
1145
  [1.7.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.1...v1.7.2
1111
1146
  [1.7.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.7.0...v1.7.1
1112
1147
  [1.7.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.6.6...v1.7.0
package/README.md CHANGED
@@ -295,7 +295,7 @@ rules: {
295
295
  | `if-statement-format` | `{` on same line as `if`/`else if`, `else` on same line as `}`, proper spacing 🔧 |
296
296
  | `multiline-if-conditions` | Conditions exceeding threshold get one operand per line with proper indentation (default: >3) 🔧 ⚙️ |
297
297
  | `no-empty-lines-in-switch-cases` | No empty line after `case X:` before code, no empty lines between cases 🔧 |
298
- | `ternary-condition-multiline` | Collapse simple ternaries to single line; expand complex conditions (>3 operands) to multiline 🔧 ⚙️ |
298
+ | `ternary-condition-multiline` | ≤maxOperands always single line; >maxOperands multiline (based on operand count, not line length) 🔧 ⚙️ |
299
299
  | **Function Rules** | |
300
300
  | `function-call-spacing` | No space between function name and `(`: `fn()` not `fn ()` 🔧 |
301
301
  | `function-declaration-style` | Auto-fix for `func-style`: converts function declarations to arrow expressions 🔧 |
@@ -1211,24 +1211,24 @@ switch (status) {
1211
1211
 
1212
1212
  ### `ternary-condition-multiline`
1213
1213
 
1214
- **What it does:** Enforces consistent ternary formatting:
1215
- - Simple ternaries (≤3 operands in condition) collapse to single line if they fit
1216
- - Complex ternaries (>3 operands) expand to multiline with each operand on its own line
1214
+ **What it does:** Formats ternary expressions based on condition operand count:
1215
+ - ≤maxOperands (default: 3): Always collapse to single line regardless of line length
1216
+ - \>maxOperands: Expand to multiline with each operand on its own line
1217
1217
 
1218
- **Why use it:** Long ternary conditions on a single line are hard to read. Breaking complex conditions into multiple lines makes them scannable.
1218
+ **Why use it:** Consistent formatting based on complexity, not line length. Simple conditions stay readable on one line; complex conditions get proper multiline formatting.
1219
1219
 
1220
1220
  **Options:**
1221
1221
 
1222
1222
  | Option | Type | Default | Description |
1223
1223
  |--------|------|---------|-------------|
1224
- | `maxOperands` | `integer` | `3` | Maximum operands to keep on single line |
1225
- | `maxLineLength` | `integer` | `120` | Maximum line length for single-line ternaries |
1224
+ | `maxOperands` | `integer` | `3` | Maximum condition operands to keep ternary on single line |
1226
1225
 
1227
1226
  ```javascript
1228
- // ✅ Good — simple condition on single line
1227
+ // ✅ Good — ≤3 operands always on single line
1229
1228
  const x = a && b && c ? "yes" : "no";
1229
+ const url = lang === "ar" ? `${apiEndpoints.exam.status}/${jobId}?lang=ar` : `${apiEndpoints.exam.status}/${jobId}`;
1230
1230
 
1231
- // ✅ Good — complex condition multiline (>3 operands)
1231
+ // ✅ Good — >3 operands formatted multiline
1232
1232
  const style = variant === "ghost"
1233
1233
  || variant === "ghost-danger"
1234
1234
  || variant === "muted"
@@ -1236,7 +1236,12 @@ const style = variant === "ghost"
1236
1236
  ? "transparent"
1237
1237
  : "solid";
1238
1238
 
1239
- // ❌ Bad — complex condition crammed on one line
1239
+ // ❌ Bad — ≤3 operands split across lines
1240
+ const x = a && b && c
1241
+ ? "yes"
1242
+ : "no";
1243
+
1244
+ // ❌ Bad — >3 operands crammed on one line
1240
1245
  const style = variant === "ghost" || variant === "ghost-danger" || variant === "muted" || variant === "primary" ? "transparent" : "solid";
1241
1246
  ```
1242
1247
 
package/index.js CHANGED
@@ -3868,21 +3868,31 @@ const multilineIfConditions = {
3868
3868
  * ───────────────────────────────────────────────────────────────
3869
3869
  *
3870
3870
  * Description:
3871
- * When a ternary condition has multiple operands (>3), format
3872
- * each operand on its own line for readability.
3871
+ * Formats ternary expressions based on condition operand count:
3872
+ * - ≤maxOperands (default: 3): Always collapse to single line
3873
+ * - >maxOperands: Format multiline with each operand on its own line
3873
3874
  *
3874
- * ✓ Good:
3875
+ * Options:
3876
+ * { maxOperands: 3 } - Maximum operands to keep on single line (default: 3)
3877
+ *
3878
+ * ✓ Good (≤3 operands - single line):
3875
3879
  * const x = a && b && c ? "yes" : "no";
3880
+ * const url = lang === "ar" ? "/ar/path" : "/en/path";
3876
3881
  *
3877
- * const x =
3878
- * variant === "ghost"
3882
+ * Good (>3 operands - multiline):
3883
+ * const x = variant === "ghost"
3879
3884
  * || variant === "ghost-danger"
3880
3885
  * || variant === "muted"
3881
3886
  * || variant === "primary"
3882
- * ? "value1"
3883
- * : "value2";
3887
+ * ? "value1"
3888
+ * : "value2";
3884
3889
  *
3885
- * ✗ Bad:
3890
+ * ✗ Bad (≤3 operands split across lines):
3891
+ * const x = a && b && c
3892
+ * ? "yes"
3893
+ * : "no";
3894
+ *
3895
+ * ✗ Bad (>3 operands on single line):
3886
3896
  * const x = variant === "ghost" || variant === "ghost-danger" || variant === "muted" || variant === "primary" ? "value1" : "value2";
3887
3897
  */
3888
3898
  const ternaryConditionMultiline = {
@@ -3890,7 +3900,6 @@ const ternaryConditionMultiline = {
3890
3900
  const sourceCode = context.sourceCode || context.getSourceCode();
3891
3901
  const options = context.options[0] || {};
3892
3902
  const maxOperands = options.maxOperands ?? 3;
3893
- const maxLineLength = options.maxLineLength ?? 120;
3894
3903
 
3895
3904
  // Check if node is wrapped in parentheses
3896
3905
  const isParenthesizedHandler = (node) => {
@@ -4048,7 +4057,7 @@ const ternaryConditionMultiline = {
4048
4057
  return false;
4049
4058
  };
4050
4059
 
4051
- // Handle simple ternaries - collapse to single line if they fit
4060
+ // Handle simple ternaries (≤maxOperands) - always collapse to single line
4052
4061
  const handleSimpleTernaryHandler = (node) => {
4053
4062
  const isOnSingleLine = node.loc.start.line === node.loc.end.line;
4054
4063
  const hasOperatorOnOwnLine = isOperatorOnOwnLineHandler(node);
@@ -4068,46 +4077,15 @@ const ternaryConditionMultiline = {
4068
4077
 
4069
4078
  // Calculate what the single line would look like
4070
4079
  const singleLineText = getTernarySingleLineHandler(node);
4071
- const indent = getLineIndentHandler(node);
4072
-
4073
- // Check if the parent needs prefix text (like "const x = " or "key: ")
4074
- let prefixLength = 0;
4075
- const parent = node.parent;
4076
-
4077
- if (parent && parent.type === "VariableDeclarator" && parent.init === node) {
4078
- // Calculate prefix based on parent's start line (where "const x = " is)
4079
- const varDecl = parent.parent;
4080
- const declKeyword = varDecl ? sourceCode.getFirstToken(varDecl).value : "const";
4081
- const varName = parent.id.name || sourceCode.getText(parent.id);
4082
-
4083
- // Prefix is "const varName = " or similar
4084
- prefixLength = declKeyword.length + 1 + varName.length + 3; // keyword + space + name + " = "
4085
- } else if (parent && parent.type === "AssignmentExpression" && parent.right === node) {
4086
- // Calculate prefix based on left side of assignment
4087
- const leftText = sourceCode.getText(parent.left);
4088
-
4089
- prefixLength = leftText.length + 3; // left + " = "
4090
- } else if (parent && parent.type === "Property" && parent.value === node) {
4091
- // Object property: key: ternary
4092
- const keyText = sourceCode.getText(parent.key);
4093
-
4094
- prefixLength = keyText.length + 2; // key + ": "
4095
- }
4096
-
4097
- // Check if single line would fit
4098
- const totalLength = indent + prefixLength + singleLineText.length + 1;
4099
-
4100
- if (totalLength <= maxLineLength) {
4101
- context.report({
4102
- fix: (fixer) => fixer.replaceText(node, singleLineText),
4103
- message: "Simple ternary should be on a single line",
4104
- node,
4105
- });
4106
4080
 
4107
- return true;
4108
- }
4081
+ // For ≤maxOperands conditions, always collapse to single line regardless of length
4082
+ context.report({
4083
+ fix: (fixer) => fixer.replaceText(node, singleLineText),
4084
+ message: `Ternary with ≤${maxOperands} operands should be on a single line`,
4085
+ node,
4086
+ });
4109
4087
 
4110
- return false;
4088
+ return true;
4111
4089
  };
4112
4090
 
4113
4091
  // Handle complex logical expressions - format multiline
@@ -4118,100 +4096,41 @@ const ternaryConditionMultiline = {
4118
4096
  const testEndLine = test.loc.end.line;
4119
4097
  const isMultiLine = testStartLine !== testEndLine;
4120
4098
 
4121
- // ≤maxOperands operands: keep condition on single line, and try to collapse whole ternary
4099
+ // ≤maxOperands operands: always collapse to single line (regardless of line length)
4122
4100
  if (operands.length <= maxOperands) {
4123
- const firstOperandStartLine = operands[0].loc.start.line;
4124
- const allOperandsStartOnSameLine = operands.every(
4125
- (op) => op.loc.start.line === firstOperandStartLine,
4126
- );
4127
-
4128
- const hasSplitBinaryExpression = operands.some(
4129
- (op) => isBinaryExpressionSplitHandler(op),
4130
- );
4131
-
4132
- // Check if ? or : is on its own line without its value
4133
- const hasOperatorOnOwnLine = isOperatorOnOwnLineHandler(node);
4134
-
4135
- // Check if ternary is multiline (could be collapsed)
4136
- const isTernaryMultiline = node.loc.start.line !== node.loc.end.line;
4137
-
4138
- // Helper to build single line condition
4139
- const buildSameLineHandler = (n) => {
4140
- if (n.type === "LogicalExpression" && !isParenthesizedHandler(n)) {
4141
- const leftText = buildSameLineHandler(n.left);
4142
- const rightText = buildSameLineHandler(n.right);
4143
-
4144
- return `${leftText} ${n.operator} ${rightText}`;
4145
- }
4146
-
4147
- if (n.type === "BinaryExpression" && isBinaryExpressionSplitHandler(n)) {
4148
- return buildBinaryExpressionSingleLineHandler(n);
4149
- }
4150
-
4151
- return getSourceTextWithGroupsHandler(n);
4152
- };
4153
-
4154
- // Check if whole ternary can fit on one line
4155
- const singleLineText = getTernarySingleLineHandler(node);
4156
- const indent = getLineIndentHandler(node);
4157
-
4158
- // Calculate prefix length for context
4159
- let prefixLength = 0;
4160
- const parent = node.parent;
4161
-
4162
- if (parent && parent.type === "VariableDeclarator" && parent.init === node) {
4163
- const varDecl = parent.parent;
4164
- const declKeyword = varDecl ? sourceCode.getFirstToken(varDecl).value : "const";
4165
- const varName = parent.id.name || sourceCode.getText(parent.id);
4166
-
4167
- prefixLength = declKeyword.length + 1 + varName.length + 3;
4168
- } else if (parent && parent.type === "AssignmentExpression" && parent.right === node) {
4169
- const leftText = sourceCode.getText(parent.left);
4170
-
4171
- prefixLength = leftText.length + 3;
4172
- } else if (parent && parent.type === "Property" && parent.value === node) {
4173
- const keyText = sourceCode.getText(parent.key);
4174
-
4175
- prefixLength = keyText.length + 2;
4176
- }
4177
-
4178
- const totalLength = indent + prefixLength + singleLineText.length + 1;
4179
- const canFitOnOneLine = totalLength <= maxLineLength;
4180
-
4181
4101
  // Skip if branches have complex objects
4182
4102
  const hasComplexBranches = hasComplexObjectHandler(node.consequent) || hasComplexObjectHandler(node.alternate);
4183
4103
 
4184
4104
  // Skip nested ternaries
4185
4105
  const hasNestedTernary = node.consequent.type === "ConditionalExpression" || node.alternate.type === "ConditionalExpression";
4186
4106
 
4187
- // Determine if we need to fix anything
4188
- const needsConditionFix = !allOperandsStartOnSameLine || hasSplitBinaryExpression;
4189
- const needsTernaryCollapse = isTernaryMultiline && canFitOnOneLine && !hasComplexBranches && !hasNestedTernary;
4190
- const needsOperatorFix = hasOperatorOnOwnLine;
4107
+ if (hasComplexBranches || hasNestedTernary) {
4108
+ return;
4109
+ }
4191
4110
 
4192
- if (needsConditionFix || needsTernaryCollapse || needsOperatorFix) {
4193
- // If whole ternary can fit on one line, collapse it
4194
- if (canFitOnOneLine && !hasComplexBranches && !hasNestedTernary) {
4195
- context.report({
4196
- fix: (fixer) => fixer.replaceText(node, singleLineText),
4197
- message: `Ternary with ≤${maxOperands} operands should be on single line when it fits`,
4198
- node,
4199
- });
4200
- } else if (needsConditionFix) {
4201
- // Otherwise just fix the condition to be on single line
4202
- context.report({
4203
- fix: (fixer) => fixer.replaceText(test, buildSameLineHandler(test)),
4204
- message: `Ternary conditions with ≤${maxOperands} operands should be single line`,
4205
- node: test,
4206
- });
4207
- }
4111
+ // Check if already properly formatted (single line)
4112
+ const isOnSingleLine = node.loc.start.line === node.loc.end.line;
4113
+ const hasOperatorOnOwnLine = isOperatorOnOwnLineHandler(node);
4114
+
4115
+ if (isOnSingleLine && !hasOperatorOnOwnLine) {
4116
+ return;
4208
4117
  }
4209
4118
 
4119
+ // Collapse to single line
4120
+ const singleLineText = getTernarySingleLineHandler(node);
4121
+
4122
+ context.report({
4123
+ fix: (fixer) => fixer.replaceText(node, singleLineText),
4124
+ message: `Ternary with ≤${maxOperands} operands should be on a single line`,
4125
+ node,
4126
+ });
4127
+
4210
4128
  return;
4211
4129
  }
4212
4130
 
4213
4131
  // More than maxOperands: each on its own line
4214
4132
  let isCorrectionNeeded = !isMultiLine;
4133
+ const parent = node.parent;
4215
4134
 
4216
4135
  if (isMultiLine) {
4217
4136
  for (let i = 0; i < operands.length - 1; i += 1) {
@@ -4257,14 +4176,57 @@ const ternaryConditionMultiline = {
4257
4176
  if (!isCorrectionNeeded && isOperatorOnOwnLineHandler(node)) {
4258
4177
  isCorrectionNeeded = true;
4259
4178
  }
4179
+
4180
+ // Check if : is on same line as ? (both should be on their own lines for multiline)
4181
+ if (!isCorrectionNeeded) {
4182
+ const questionToken = sourceCode.getTokenAfter(test, (t) => t.value === "?");
4183
+ const colonToken = sourceCode.getTokenAfter(node.consequent, (t) => t.value === ":");
4184
+
4185
+ if (questionToken && colonToken && questionToken.loc.start.line === colonToken.loc.start.line) {
4186
+ isCorrectionNeeded = true;
4187
+ }
4188
+ }
4189
+
4190
+ // Check if first operand is not on same line as parent property key
4191
+ if (!isCorrectionNeeded && parent && parent.type === "Property" && parent.value === node) {
4192
+ const keyEndLine = parent.key.loc.end.line;
4193
+ const firstOperandLine = operands[0].loc.start.line;
4194
+
4195
+ if (firstOperandLine !== keyEndLine) {
4196
+ isCorrectionNeeded = true;
4197
+ }
4198
+ }
4260
4199
  }
4261
4200
 
4262
4201
  if (isCorrectionNeeded) {
4263
4202
  context.report({
4264
4203
  fix: (fixer) => {
4265
- // Get the indentation based on where the ternary starts
4266
- const lineText = sourceCode.lines[node.loc.start.line - 1];
4267
- const baseIndent = lineText.match(/^\s*/)[0];
4204
+ // Get proper base indent
4205
+ let baseIndent;
4206
+ let includePropertyKey = false;
4207
+ let propertyKeyText = "";
4208
+
4209
+ // Check if parent is Property and value starts on different line
4210
+ if (parent && parent.type === "Property" && parent.value === node) {
4211
+ const keyEndLine = parent.key.loc.end.line;
4212
+ const firstOperandLine = operands[0].loc.start.line;
4213
+
4214
+ if (firstOperandLine !== keyEndLine) {
4215
+ // Need to include property key in fix
4216
+ includePropertyKey = true;
4217
+ propertyKeyText = sourceCode.getText(parent.key) + ": ";
4218
+ const propertyLineText = sourceCode.lines[parent.loc.start.line - 1];
4219
+
4220
+ baseIndent = propertyLineText.match(/^\s*/)[0];
4221
+ }
4222
+ }
4223
+
4224
+ if (!baseIndent) {
4225
+ const lineText = sourceCode.lines[node.loc.start.line - 1];
4226
+
4227
+ baseIndent = lineText.match(/^\s*/)[0];
4228
+ }
4229
+
4268
4230
  const conditionIndent = baseIndent + " ";
4269
4231
 
4270
4232
  const buildMultilineHandler = (n) => {
@@ -4281,9 +4243,17 @@ const ternaryConditionMultiline = {
4281
4243
  const consequentText = sourceCode.getText(node.consequent);
4282
4244
  const alternateText = sourceCode.getText(node.alternate);
4283
4245
 
4284
- // No leading newline - keep first operand on same line as whatever precedes it
4285
- // Use conditionIndent for ? and : to align with || operators
4286
- const newText = `${buildMultilineHandler(test)}\n${conditionIndent}? ${consequentText}\n${conditionIndent}: ${alternateText}`;
4246
+ // Build multiline with ? and : each on their own lines
4247
+ const conditionPart = buildMultilineHandler(test);
4248
+ const newText = `${conditionPart}\n${conditionIndent}? ${consequentText}\n${conditionIndent}: ${alternateText}`;
4249
+
4250
+ if (includePropertyKey) {
4251
+ // Replace the entire property value including fixing the key position
4252
+ return fixer.replaceTextRange(
4253
+ [parent.key.range[0], node.range[1]],
4254
+ `${propertyKeyText}${newText}`,
4255
+ );
4256
+ }
4287
4257
 
4288
4258
  return fixer.replaceText(node, newText);
4289
4259
  },
@@ -4310,21 +4280,15 @@ const ternaryConditionMultiline = {
4310
4280
  };
4311
4281
  },
4312
4282
  meta: {
4313
- docs: { description: "Enforce consistent ternary formatting: collapse simple ternaries to single line, expand complex conditions to multiline" },
4283
+ docs: { description: "Enforce consistent ternary formatting based on condition operand count: ≤maxOperands collapses to single line, >maxOperands expands to multiline" },
4314
4284
  fixable: "code",
4315
4285
  schema: [
4316
4286
  {
4317
4287
  additionalProperties: false,
4318
4288
  properties: {
4319
- maxLineLength: {
4320
- default: 120,
4321
- description: "Maximum line length for single-line ternaries (default: 120)",
4322
- minimum: 80,
4323
- type: "integer",
4324
- },
4325
4289
  maxOperands: {
4326
4290
  default: 3,
4327
- description: "Maximum operands to keep on single line (default: 3)",
4291
+ description: "Maximum condition operands to keep ternary on single line (default: 3). Ternaries with more operands are formatted multiline.",
4328
4292
  minimum: 1,
4329
4293
  type: "integer",
4330
4294
  },
@@ -9747,10 +9711,10 @@ const noEmptyLinesInFunctionParams = {
9747
9711
  }
9748
9712
  }
9749
9713
 
9750
- // Find the closing brace of ObjectPattern
9751
- const closeBrace = sourceCode.getLastToken(param);
9714
+ // Find the closing brace of ObjectPattern (first } after last prop, not last token which could be from type annotation)
9715
+ const closeBrace = sourceCode.getTokenAfter(lastProp, (t) => t.value === "}");
9752
9716
 
9753
- if (closeBrace && closeBrace.value === "}") {
9717
+ if (closeBrace) {
9754
9718
  // Check for empty line before closing brace
9755
9719
  if (closeBrace.loc.start.line - lastProp.loc.end.line > 1) {
9756
9720
  context.report({
@@ -9788,14 +9752,74 @@ const noEmptyLinesInFunctionParams = {
9788
9752
  });
9789
9753
  };
9790
9754
 
9755
+ // Check TSTypeLiteral for empty lines (type annotation objects like { prop: Type })
9756
+ const checkTypeLiteralHandler = (node) => {
9757
+ if (!node.members || node.members.length === 0) return;
9758
+
9759
+ const firstMember = node.members[0];
9760
+ const lastMember = node.members[node.members.length - 1];
9761
+
9762
+ // Find opening brace
9763
+ const openBrace = sourceCode.getFirstToken(node);
9764
+
9765
+ if (openBrace && openBrace.value === "{") {
9766
+ // Check for empty line after opening brace
9767
+ if (firstMember.loc.start.line - openBrace.loc.end.line > 1) {
9768
+ context.report({
9769
+ fix: (fixer) => fixer.replaceTextRange(
9770
+ [openBrace.range[1], firstMember.range[0]],
9771
+ "\n" + " ".repeat(firstMember.loc.start.column),
9772
+ ),
9773
+ message: "No empty line after opening brace in type definition",
9774
+ node: firstMember,
9775
+ });
9776
+ }
9777
+ }
9778
+
9779
+ // Find closing brace
9780
+ const closeBrace = sourceCode.getLastToken(node);
9781
+
9782
+ if (closeBrace && closeBrace.value === "}") {
9783
+ // Check for empty line before closing brace
9784
+ if (closeBrace.loc.start.line - lastMember.loc.end.line > 1) {
9785
+ context.report({
9786
+ fix: (fixer) => fixer.replaceTextRange(
9787
+ [lastMember.range[1], closeBrace.range[0]],
9788
+ "\n" + " ".repeat(closeBrace.loc.start.column),
9789
+ ),
9790
+ message: "No empty line before closing brace in type definition",
9791
+ node: lastMember,
9792
+ });
9793
+ }
9794
+ }
9795
+
9796
+ // Check for empty lines between members
9797
+ for (let i = 0; i < node.members.length - 1; i += 1) {
9798
+ const current = node.members[i];
9799
+ const next = node.members[i + 1];
9800
+
9801
+ if (next.loc.start.line - current.loc.end.line > 1) {
9802
+ context.report({
9803
+ fix: (fixer) => fixer.replaceTextRange(
9804
+ [current.range[1], next.range[0]],
9805
+ "\n" + " ".repeat(next.loc.start.column),
9806
+ ),
9807
+ message: "No empty lines between type members",
9808
+ node: next,
9809
+ });
9810
+ }
9811
+ }
9812
+ };
9813
+
9791
9814
  return {
9792
9815
  ArrowFunctionExpression: checkFunctionHandler,
9793
9816
  FunctionDeclaration: checkFunctionHandler,
9794
9817
  FunctionExpression: checkFunctionHandler,
9818
+ TSTypeLiteral: checkTypeLiteralHandler,
9795
9819
  };
9796
9820
  },
9797
9821
  meta: {
9798
- docs: { description: "Disallow empty lines in function parameters" },
9822
+ docs: { description: "Disallow empty lines in function parameters and type definitions" },
9799
9823
  fixable: "whitespace",
9800
9824
  schema: [],
9801
9825
  type: "layout",
@@ -14993,7 +15017,7 @@ const noInlineTypeDefinitions = {
14993
15017
  const memberCount = countUnionMembersHandler(typeNode);
14994
15018
  const typeText = sourceCode.getText(typeNode);
14995
15019
 
14996
- if (memberCount > maxUnionMembers || typeText.length > maxLength) {
15020
+ if (memberCount >= maxUnionMembers || typeText.length > maxLength) {
14997
15021
  context.report({
14998
15022
  message: `Inline union type with ${memberCount} members is too complex. Extract to a named type in a types file.`,
14999
15023
  node: typeNode,
@@ -15023,7 +15047,7 @@ const noInlineTypeDefinitions = {
15023
15047
  const memberCount = countUnionMembersHandler(propType);
15024
15048
  const typeText = sourceCode.getText(propType);
15025
15049
 
15026
- if (memberCount > maxUnionMembers || typeText.length > maxLength) {
15050
+ if (memberCount >= maxUnionMembers || typeText.length > maxLength) {
15027
15051
  context.report({
15028
15052
  message: `Property "${propName}" has inline union type with ${memberCount} members. Extract to a named type in a types file.`,
15029
15053
  node: propType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.7.3",
3
+ "version": "1.7.6",
4
4
  "description": "A custom ESLint plugin for enforcing consistent code formatting and style rules in React/JSX projects",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",