eslint-plugin-code-style 1.11.3 → 1.11.4

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 (3) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/index.js +159 -0
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.11.4] - 2026-02-04
11
+
12
+ ### Fixed
13
+
14
+ - **`opening-brackets-same-line`**
15
+ - Collapse JSX elements with simple children to single line (e.g., `<span>{strings.label}</span>`)
16
+ - Handle simple LogicalExpression children (e.g., `<p>{user?.email || fallback}</p>`)
17
+
18
+ - **`jsx-children-on-new-line`** / **`jsx-element-child-new-line`**
19
+ - Recognize simple LogicalExpression (≤2 operands) as simple children
20
+ - Recognize ChainExpression (optional chaining like `user?.name`) as simple expression
21
+ - Prevent circular fix conflicts with `opening-brackets-same-line`
22
+
23
+ ---
24
+
10
25
  ## [1.11.3] - 2026-02-04
11
26
 
12
27
  ### Fixed
@@ -1489,6 +1504,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1489
1504
 
1490
1505
  ---
1491
1506
 
1507
+ [1.11.4]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.11.3...v1.11.4
1492
1508
  [1.11.3]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.11.2...v1.11.3
1493
1509
  [1.11.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.11.1...v1.11.2
1494
1510
  [1.11.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.11.0...v1.11.1
package/index.js CHANGED
@@ -7683,6 +7683,12 @@ const jsxChildrenOnNewLine = {
7683
7683
 
7684
7684
  if (expr.type === "Identifier") return true;
7685
7685
  if (expr.type === "Literal") return true;
7686
+
7687
+ // Handle ChainExpression (optional chaining like user?.name)
7688
+ if (expr.type === "ChainExpression") {
7689
+ return isSimpleExpressionHandler(expr.expression);
7690
+ }
7691
+
7686
7692
  if (expr.type === "MemberExpression") {
7687
7693
  // Allow nested member expressions like row.original.currency or row[field]
7688
7694
  let current = expr;
@@ -7700,6 +7706,11 @@ const jsxChildrenOnNewLine = {
7700
7706
  current = current.object;
7701
7707
  }
7702
7708
 
7709
+ // Handle ChainExpression at the end of the chain
7710
+ if (current.type === "ChainExpression") {
7711
+ return isSimpleExpressionHandler(current.expression);
7712
+ }
7713
+
7703
7714
  return current.type === "Identifier";
7704
7715
  }
7705
7716
 
@@ -7719,6 +7730,32 @@ const jsxChildrenOnNewLine = {
7719
7730
  return false;
7720
7731
  }
7721
7732
 
7733
+ // Allow simple LogicalExpression (2 operands with simple left/right)
7734
+ if (expr.type === "LogicalExpression") {
7735
+ // Count operands - if more than 2, not simple
7736
+ const countOperands = (n) => {
7737
+ if (n.type === "LogicalExpression") {
7738
+ return countOperands(n.left) + countOperands(n.right);
7739
+ }
7740
+
7741
+ return 1;
7742
+ };
7743
+
7744
+ if (countOperands(expr) > 2) return false;
7745
+
7746
+ // Check if left and right are simple
7747
+ const isSimpleSide = (n) => {
7748
+ if (n.type === "Identifier") return true;
7749
+ if (n.type === "Literal") return true;
7750
+ if (n.type === "MemberExpression") return isSimpleExpressionHandler(n);
7751
+ if (n.type === "ChainExpression" && n.expression) return isSimpleSide(n.expression);
7752
+
7753
+ return false;
7754
+ };
7755
+
7756
+ return isSimpleSide(expr.left) && isSimpleSide(expr.right);
7757
+ }
7758
+
7722
7759
  return false;
7723
7760
  };
7724
7761
 
@@ -7944,6 +7981,12 @@ const jsxElementChildNewLine = {
7944
7981
 
7945
7982
  if (expr.type === "Identifier") return true;
7946
7983
  if (expr.type === "Literal") return true;
7984
+
7985
+ // Handle ChainExpression (optional chaining like user?.name)
7986
+ if (expr.type === "ChainExpression") {
7987
+ return isSimpleExpressionHandler(expr.expression);
7988
+ }
7989
+
7947
7990
  if (expr.type === "MemberExpression") {
7948
7991
  // Allow nested member expressions like row.original.currency or row[field]
7949
7992
  let current = expr;
@@ -7961,6 +8004,11 @@ const jsxElementChildNewLine = {
7961
8004
  current = current.object;
7962
8005
  }
7963
8006
 
8007
+ // Handle ChainExpression at the end of the chain
8008
+ if (current.type === "ChainExpression") {
8009
+ return isSimpleExpressionHandler(current.expression);
8010
+ }
8011
+
7964
8012
  return current.type === "Identifier";
7965
8013
  }
7966
8014
 
@@ -7980,6 +8028,32 @@ const jsxElementChildNewLine = {
7980
8028
  return false;
7981
8029
  }
7982
8030
 
8031
+ // Allow simple LogicalExpression (2 operands with simple left/right)
8032
+ if (expr.type === "LogicalExpression") {
8033
+ // Count operands - if more than 2, not simple
8034
+ const countOperands = (n) => {
8035
+ if (n.type === "LogicalExpression") {
8036
+ return countOperands(n.left) + countOperands(n.right);
8037
+ }
8038
+
8039
+ return 1;
8040
+ };
8041
+
8042
+ if (countOperands(expr) > 2) return false;
8043
+
8044
+ // Check if left and right are simple
8045
+ const isSimpleSide = (n) => {
8046
+ if (n.type === "Identifier") return true;
8047
+ if (n.type === "Literal") return true;
8048
+ if (n.type === "MemberExpression") return isSimpleExpressionHandler(n);
8049
+ if (n.type === "ChainExpression" && n.expression) return isSimpleSide(n.expression);
8050
+
8051
+ return false;
8052
+ };
8053
+
8054
+ return isSimpleSide(expr.left) && isSimpleSide(expr.right);
8055
+ }
8056
+
7983
8057
  return false;
7984
8058
  };
7985
8059
 
@@ -13117,6 +13191,49 @@ const openingBracketsSameLine = {
13117
13191
  message: "Simple expression should be on single line in JSX attribute",
13118
13192
  node: expression,
13119
13193
  });
13194
+
13195
+ return;
13196
+ }
13197
+
13198
+ // Check if parent JSX element should be collapsed to single line
13199
+ // e.g., <span>\n {strings.label}\n</span> → <span>{strings.label}</span>
13200
+ const parent = node.parent;
13201
+
13202
+ if (parent && parent.type === "JSXElement") {
13203
+ const children = parent.children.filter(
13204
+ (child) => !(child.type === "JSXText" && /^\s*$/.test(child.value)),
13205
+ );
13206
+
13207
+ // Only collapse if this expression is the only meaningful child
13208
+ if (children.length === 1 && children[0] === node) {
13209
+ const openingTag = parent.openingElement;
13210
+ const closingTag = parent.closingElement;
13211
+
13212
+ if (closingTag) {
13213
+ const openTagEnd = openingTag.loc.end.line;
13214
+ const closeTagStart = closingTag.loc.start.line;
13215
+
13216
+ // Check if element spans multiple lines but content is simple
13217
+ if (openTagEnd !== closeTagStart) {
13218
+ const openTagText = sourceCode.getText(openingTag);
13219
+ const closeTagText = sourceCode.getText(closingTag);
13220
+ const expressionText = sourceCode.getText(node);
13221
+ const collapsedLength = openTagText.length + expressionText.length + closeTagText.length;
13222
+
13223
+ // Only collapse if total length is reasonable
13224
+ if (collapsedLength <= 120) {
13225
+ context.report({
13226
+ fix: (fixer) => fixer.replaceTextRange(
13227
+ [openingTag.range[1], closingTag.range[0]],
13228
+ expressionText,
13229
+ ),
13230
+ message: "JSX element with simple expression should be on single line",
13231
+ node: parent,
13232
+ });
13233
+ }
13234
+ }
13235
+ }
13236
+ }
13120
13237
  }
13121
13238
 
13122
13239
  return;
@@ -13280,6 +13397,48 @@ const openingBracketsSameLine = {
13280
13397
  message: "Simple logical expression should be on a single line",
13281
13398
  node: expression,
13282
13399
  });
13400
+
13401
+ return;
13402
+ }
13403
+
13404
+ // Check if parent JSX element should be collapsed to single line
13405
+ const parent = node.parent;
13406
+
13407
+ if (parent && parent.type === "JSXElement") {
13408
+ const children = parent.children.filter(
13409
+ (child) => !(child.type === "JSXText" && /^\s*$/.test(child.value)),
13410
+ );
13411
+
13412
+ // Only collapse if this expression is the only meaningful child
13413
+ if (children.length === 1 && children[0] === node) {
13414
+ const openingTag = parent.openingElement;
13415
+ const closingTag = parent.closingElement;
13416
+
13417
+ if (closingTag) {
13418
+ const openTagEnd = openingTag.loc.end.line;
13419
+ const closeTagStart = closingTag.loc.start.line;
13420
+
13421
+ // Check if element spans multiple lines but content is simple
13422
+ if (openTagEnd !== closeTagStart) {
13423
+ const openTagText = sourceCode.getText(openingTag);
13424
+ const closeTagText = sourceCode.getText(closingTag);
13425
+ const collapsedExpr = "{" + collapsedText + "}";
13426
+ const collapsedLength = openTagText.length + collapsedExpr.length + closeTagText.length;
13427
+
13428
+ // Only collapse if total length is reasonable
13429
+ if (collapsedLength <= 120) {
13430
+ context.report({
13431
+ fix: (fixer) => fixer.replaceTextRange(
13432
+ [openingTag.range[1], closingTag.range[0]],
13433
+ collapsedExpr,
13434
+ ),
13435
+ message: "JSX element with simple logical expression should be on single line",
13436
+ node: parent,
13437
+ });
13438
+ }
13439
+ }
13440
+ }
13441
+ }
13283
13442
  }
13284
13443
 
13285
13444
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.11.3",
3
+ "version": "1.11.4",
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",