eslint-plugin-code-style 1.14.1 → 1.14.3

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 +23 -0
  2. package/index.js +134 -1
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.14.3] - 2026-02-05
11
+
12
+ ### Enhanced
13
+
14
+ - **`type-annotation-spacing`** - Add auto-fix to collapse function types with 2 or fewer params to one line
15
+ - **`interface-format`** - Fix circular fix conflict by skipping collapse when property has multi-line function type
16
+
17
+ ---
18
+
19
+ ## [1.14.2] - 2026-02-05
20
+
21
+ ### Enhanced
22
+
23
+ - **`type-annotation-spacing`** - Add spacing rules for async keyword and function types:
24
+ - Enforce space after `async` keyword: `async()` → `async ()`
25
+ - Enforce space after `=>` in function types: `() =>void` → `() => void`
26
+ - Format function types with 3+ params on multiple lines
27
+ - **`interface-format`** - Skip collapsing single-property interfaces when property has function type with 3+ params
28
+
29
+ ---
30
+
10
31
  ## [1.14.1] - 2026-02-05
11
32
 
12
33
  ### Enhanced
@@ -1666,6 +1687,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1666
1687
 
1667
1688
  ---
1668
1689
 
1690
+ [1.14.3]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.14.2...v1.14.3
1691
+ [1.14.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.14.1...v1.14.2
1669
1692
  [1.14.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.14.0...v1.14.1
1670
1693
  [1.14.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.13.0...v1.14.0
1671
1694
  [1.13.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.12.1...v1.13.0
package/index.js CHANGED
@@ -19980,6 +19980,57 @@ const typeAnnotationSpacing = {
19980
19980
  const sourceCode = context.sourceCode || context.getSourceCode();
19981
19981
 
19982
19982
  return {
19983
+ ArrowFunctionExpression(node) {
19984
+ // Check for space after async keyword: async() => -> async () =>
19985
+ if (node.async) {
19986
+ const asyncToken = sourceCode.getFirstToken(node, (t) => t.value === "async");
19987
+ const openParen = sourceCode.getTokenAfter(asyncToken, (t) => t.value === "(");
19988
+
19989
+ if (asyncToken && openParen) {
19990
+ const textBetween = sourceCode.text.slice(asyncToken.range[1], openParen.range[0]);
19991
+
19992
+ // Should have exactly one space after async
19993
+ if (textBetween === "") {
19994
+ context.report({
19995
+ fix: (fixer) => fixer.insertTextAfter(asyncToken, " "),
19996
+ message: "Missing space after async keyword",
19997
+ node: asyncToken,
19998
+ });
19999
+ } else if (textBetween !== " " && !textBetween.includes("\n")) {
20000
+ // Has extra spaces but not newline
20001
+ context.report({
20002
+ fix: (fixer) => fixer.replaceTextRange([asyncToken.range[1], openParen.range[0]], " "),
20003
+ message: "Should have exactly one space after async keyword",
20004
+ node: asyncToken,
20005
+ });
20006
+ }
20007
+ }
20008
+ }
20009
+ },
20010
+ FunctionExpression(node) {
20011
+ // Check for space after async keyword in function expressions: async function() -> async function ()
20012
+ if (node.async) {
20013
+ const asyncToken = sourceCode.getFirstToken(node, (t) => t.value === "async");
20014
+ const functionToken = sourceCode.getTokenAfter(asyncToken, (t) => t.value === "function");
20015
+
20016
+ if (functionToken) {
20017
+ const openParen = sourceCode.getTokenAfter(functionToken, (t) => t.value === "(");
20018
+
20019
+ if (openParen) {
20020
+ const textBetween = sourceCode.text.slice(functionToken.range[1], openParen.range[0]);
20021
+
20022
+ // Should have exactly one space after function keyword
20023
+ if (textBetween === "") {
20024
+ context.report({
20025
+ fix: (fixer) => fixer.insertTextAfter(functionToken, " "),
20026
+ message: "Missing space after function keyword",
20027
+ node: functionToken,
20028
+ });
20029
+ }
20030
+ }
20031
+ }
20032
+ }
20033
+ },
19983
20034
  TSArrayType(node) {
19984
20035
  // Check for space before [] like: Type []
19985
20036
  const elementType = node.elementType;
@@ -20365,6 +20416,83 @@ const typeAnnotationSpacing = {
20365
20416
  }
20366
20417
  }
20367
20418
  },
20419
+ TSFunctionType(node) {
20420
+ // Check for space after => in function types: () =>void -> () => void
20421
+ // Find the arrow token by searching all tokens in the node
20422
+ const tokens = sourceCode.getTokens(node);
20423
+ const arrowToken = tokens.find((t) => t.value === "=>");
20424
+
20425
+ if (arrowToken) {
20426
+ const nextToken = sourceCode.getTokenAfter(arrowToken);
20427
+
20428
+ if (nextToken) {
20429
+ const textAfterArrow = sourceCode.text.slice(arrowToken.range[1], nextToken.range[0]);
20430
+
20431
+ // Should have exactly one space after =>
20432
+ if (textAfterArrow === "") {
20433
+ context.report({
20434
+ fix: (fixer) => fixer.insertTextAfter(arrowToken, " "),
20435
+ message: "Missing space after => in function type",
20436
+ node: arrowToken,
20437
+ });
20438
+ } else if (textAfterArrow !== " " && !textAfterArrow.includes("\n")) {
20439
+ // Has extra spaces but not newline
20440
+ context.report({
20441
+ fix: (fixer) => fixer.replaceTextRange([arrowToken.range[1], nextToken.range[0]], " "),
20442
+ message: "Should have exactly one space after => in function type",
20443
+ node: arrowToken,
20444
+ });
20445
+ }
20446
+ }
20447
+ }
20448
+
20449
+ // Check function type params formatting
20450
+ // - 3+ params should be multiline
20451
+ // - 0-2 params should be on one line
20452
+ const params = node.params;
20453
+ const openParen = tokens.find((t) => t.value === "(");
20454
+
20455
+ if (openParen && arrowToken) {
20456
+ const closeParen = sourceCode.getTokenBefore(arrowToken, (t) => t.value === ")");
20457
+
20458
+ if (closeParen) {
20459
+ const isMultiLine = openParen.loc.start.line !== closeParen.loc.end.line;
20460
+
20461
+ if (params && params.length >= 3 && !isMultiLine) {
20462
+ // 3+ params on one line - expand to multiple lines
20463
+ const lineStart = sourceCode.text.lastIndexOf("\n", node.range[0]) + 1;
20464
+ const lineText = sourceCode.text.slice(lineStart, node.range[0]);
20465
+ const match = lineText.match(/^(\s*)/);
20466
+ const baseIndent = match ? match[1] : "";
20467
+ const paramIndent = baseIndent + " ";
20468
+
20469
+ const formattedParams = params.map((p) => {
20470
+ const paramText = sourceCode.getText(p);
20471
+
20472
+ return paramIndent + paramText;
20473
+ }).join(",\n");
20474
+
20475
+ const newParamsText = `(\n${formattedParams},\n${baseIndent})`;
20476
+
20477
+ context.report({
20478
+ fix: (fixer) => fixer.replaceTextRange([openParen.range[0], closeParen.range[1]], newParamsText),
20479
+ message: "Function type with 3+ parameters should have each parameter on its own line",
20480
+ node,
20481
+ });
20482
+ } else if (params && params.length <= 2 && isMultiLine) {
20483
+ // 0-2 params on multiple lines - collapse to one line
20484
+ const formattedParams = params.map((p) => sourceCode.getText(p).trim()).join(", ");
20485
+ const newParamsText = `(${formattedParams})`;
20486
+
20487
+ context.report({
20488
+ fix: (fixer) => fixer.replaceTextRange([openParen.range[0], closeParen.range[1]], newParamsText),
20489
+ message: "Function type with 2 or fewer parameters should be on one line",
20490
+ node,
20491
+ });
20492
+ }
20493
+ }
20494
+ }
20495
+ },
20368
20496
  VariableDeclarator(node) {
20369
20497
  // Check for space before generic like: ColumnDef <T>
20370
20498
  if (node.id && node.id.typeAnnotation) {
@@ -21833,6 +21961,7 @@ const interfaceFormat = {
21833
21961
 
21834
21962
  // For single member, should be on one line without trailing punctuation
21835
21963
  // But skip if the property has a nested object type with 2+ members
21964
+ // Or if the property has a multi-line function type (let type-annotation-spacing handle it first)
21836
21965
  if (members.length === 1) {
21837
21966
  const member = members[0];
21838
21967
  const isMultiLine = openBraceToken.loc.end.line !== closeBraceToken.loc.start.line;
@@ -21843,7 +21972,11 @@ const interfaceFormat = {
21843
21972
  const hasMultiMemberNestedType = hasNestedType && nestedType.members?.length >= 2;
21844
21973
  const hasSingleMemberNestedType = hasNestedType && nestedType.members?.length === 1;
21845
21974
 
21846
- if (isMultiLine && !hasMultiMemberNestedType) {
21975
+ // Check if property has function type that spans multiple lines
21976
+ const hasMultiLineFunctionType = nestedType?.type === "TSFunctionType" &&
21977
+ nestedType.loc.start.line !== nestedType.loc.end.line;
21978
+
21979
+ if (isMultiLine && !hasMultiMemberNestedType && !hasMultiLineFunctionType) {
21847
21980
  // Build the collapsed text, handling nested types specially
21848
21981
  let cleanText;
21849
21982
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.14.1",
3
+ "version": "1.14.3",
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",