eslint-plugin-code-style 1.3.9 → 1.3.10

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 (2) hide show
  1. package/index.js +110 -46
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1836,6 +1836,47 @@ const functionNamingConvention = {
1836
1836
 
1837
1837
  const endsWithHandler = (name) => handlerRegex.test(name);
1838
1838
 
1839
+ // Check if a node contains JSX (making it a React component)
1840
+ const containsJsxHandler = (node) => {
1841
+ if (!node) return false;
1842
+
1843
+ if (node.type === "JSXElement" || node.type === "JSXFragment") return true;
1844
+
1845
+ if (node.type === "BlockStatement") {
1846
+ for (const statement of node.body) {
1847
+ if (statement.type === "ReturnStatement" && statement.argument) {
1848
+ if (containsJsxHandler(statement.argument)) return true;
1849
+ }
1850
+
1851
+ if (statement.type === "IfStatement") {
1852
+ if (containsJsxHandler(statement.consequent)) return true;
1853
+ if (statement.alternate && containsJsxHandler(statement.alternate)) return true;
1854
+ }
1855
+ }
1856
+ }
1857
+
1858
+ if (node.type === "ConditionalExpression") {
1859
+ return containsJsxHandler(node.consequent) || containsJsxHandler(node.alternate);
1860
+ }
1861
+
1862
+ if (node.type === "LogicalExpression") {
1863
+ return containsJsxHandler(node.left) || containsJsxHandler(node.right);
1864
+ }
1865
+
1866
+ if (node.type === "ParenthesizedExpression") {
1867
+ return containsJsxHandler(node.expression);
1868
+ }
1869
+
1870
+ return false;
1871
+ };
1872
+
1873
+ // Check if function is a React component (PascalCase + returns JSX)
1874
+ const isReactComponentHandler = (node, name) => {
1875
+ if (!name || !/^[A-Z]/.test(name)) return false;
1876
+
1877
+ return containsJsxHandler(node.body);
1878
+ };
1879
+
1839
1880
  const checkFunctionHandler = (node) => {
1840
1881
  let name = null;
1841
1882
 
@@ -1852,7 +1893,10 @@ const functionNamingConvention = {
1852
1893
  // Skip hooks
1853
1894
  if (/^use[A-Z]/.test(name)) return;
1854
1895
 
1855
- // Check PascalCase functions
1896
+ // Skip React components (PascalCase + returns JSX)
1897
+ if (isReactComponentHandler(node, name)) return;
1898
+
1899
+ // Check PascalCase functions that are NOT React components
1856
1900
  if (/^[A-Z]/.test(name)) {
1857
1901
  // If starts with a verb (case-insensitive), it should be camelCase
1858
1902
  if (startsWithVerbCaseInsensitiveHandler(name)) {
@@ -13895,32 +13939,27 @@ const reactCodeOrder = {
13895
13939
  }
13896
13940
  }
13897
13941
 
13898
- // Filter to only relevant statements (skip comments, empty statements)
13899
- const relevantStatements = statements.filter((s) => s.type === "VariableDeclaration"
13942
+ // Categorizable statement types for ordering
13943
+ const isCategorizableStatement = (s) => s.type === "VariableDeclaration"
13900
13944
  || s.type === "FunctionDeclaration"
13901
13945
  || s.type === "ExpressionStatement"
13902
- || s.type === "ReturnStatement");
13946
+ || s.type === "ReturnStatement";
13947
+
13948
+ // Filter to only categorizable statements for order checking
13949
+ const categorizableStatements = statements.filter(isCategorizableStatement);
13903
13950
 
13904
- if (relevantStatements.length < 2) return;
13951
+ if (categorizableStatements.length < 2) return;
13905
13952
 
13906
- // Collect all statements with their categories and original positions
13907
- const statementsWithCategories = [];
13953
+ // Check order violations using only categorizable statements
13908
13954
  let hasOrderViolation = false;
13909
13955
  let lastCategory = 0;
13910
13956
  let violatingStatement = null;
13911
13957
  let violatingCategory = null;
13912
13958
  let previousCategory = null;
13913
13959
 
13914
- for (let i = 0; i < relevantStatements.length; i++) {
13915
- const statement = relevantStatements[i];
13960
+ for (const statement of categorizableStatements) {
13916
13961
  const category = getStatementCategoryHandler(statement, propNames);
13917
13962
 
13918
- statementsWithCategories.push({
13919
- category,
13920
- index: i,
13921
- statement,
13922
- });
13923
-
13924
13963
  // Skip unknown statements for order checking
13925
13964
  if (category === ORDER.UNKNOWN) continue;
13926
13965
 
@@ -13937,53 +13976,78 @@ const reactCodeOrder = {
13937
13976
 
13938
13977
  if (!hasOrderViolation) return;
13939
13978
 
13940
- // Sort statements by category (stable sort - maintains relative order within same category)
13941
- const sortedStatements = [...statementsWithCategories].sort((a, b) => {
13942
- if (a.category !== b.category) {
13943
- return a.category - b.category;
13979
+ // For auto-fix, process ALL statements and assign sort keys
13980
+ // Non-categorizable statements (if, for, while, etc.) get the category of the NEXT categorizable statement
13981
+ // This keeps them positioned just before whatever comes after them
13982
+ const allStatementsWithSortKeys = [];
13983
+
13984
+ // First pass: assign categories to categorizable statements
13985
+ const categoryMap = new Map();
13986
+
13987
+ for (const stmt of statements) {
13988
+ if (isCategorizableStatement(stmt)) {
13989
+ categoryMap.set(stmt, getStatementCategoryHandler(stmt, propNames));
13944
13990
  }
13991
+ }
13992
+
13993
+ // Second pass: assign sort keys to all statements
13994
+ // Non-categorizable statements get the category of the next categorizable statement
13995
+ for (let i = 0; i < statements.length; i++) {
13996
+ const stmt = statements[i];
13997
+ let sortKey;
13998
+
13999
+ if (isCategorizableStatement(stmt)) {
14000
+ sortKey = categoryMap.get(stmt);
14001
+ } else {
14002
+ // Find the next categorizable statement's category
14003
+ sortKey = ORDER.RETURN; // Default to RETURN if none found
14004
+
14005
+ for (let j = i + 1; j < statements.length; j++) {
14006
+ if (isCategorizableStatement(statements[j])) {
14007
+ sortKey = categoryMap.get(statements[j]);
13945
14008
 
13946
- // Maintain original order within the same category
14009
+ break;
14010
+ }
14011
+ }
14012
+ }
14013
+
14014
+ allStatementsWithSortKeys.push({
14015
+ index: i,
14016
+ sortKey,
14017
+ statement: stmt,
14018
+ });
14019
+ }
14020
+
14021
+ // Sort all statements by sort key (stable sort - maintains relative order within same key)
14022
+ const sortedStatements = [...allStatementsWithSortKeys].sort((a, b) => {
14023
+ if (a.sortKey !== b.sortKey) {
14024
+ return a.sortKey - b.sortKey;
14025
+ }
14026
+
14027
+ // Maintain original order within the same sort key
13947
14028
  return a.index - b.index;
13948
14029
  });
13949
14030
 
13950
14031
  // Check if sorting actually changes the order
13951
- const orderChanged = sortedStatements.some((s, i) => s.index !== statementsWithCategories[i].index);
14032
+ const orderChanged = sortedStatements.some((s, i) => s.index !== i);
13952
14033
 
13953
14034
  if (!orderChanged) return;
13954
14035
 
13955
14036
  // Build the fix
13956
14037
  const fixHandler = (fixer) => {
13957
14038
  // Get the base indentation from the first statement
13958
- const firstStatementLine = sourceCode.lines[relevantStatements[0].loc.start.line - 1];
14039
+ const firstStatementLine = sourceCode.lines[statements[0].loc.start.line - 1];
13959
14040
  const baseIndent = firstStatementLine.match(/^\s*/)[0];
13960
14041
 
13961
- // Get statement text with its leading comments
13962
- const getStatementTextWithCommentsHandler = (stmt, stmtIndex) => {
13963
- // Find the previous statement to determine comment boundaries
13964
- const prevStmtIndex = relevantStatements.findIndex((s) => s === stmt) - 1;
13965
- const prevStmt = prevStmtIndex >= 0 ? relevantStatements[prevStmtIndex] : null;
13966
- const prevEnd = prevStmt ? prevStmt.range[1] : sourceCode.getFirstToken(body).range[1];
13967
-
13968
- // Get text from after previous statement to end of current statement
13969
- // This includes leading whitespace and comments
13970
- const fullText = sourceCode.text.slice(prevEnd, stmt.range[1]);
13971
-
13972
- // Trim leading newlines but keep indentation of the statement
13973
- const trimmed = fullText.replace(/^\s*\n/, "");
13974
-
13975
- return trimmed;
13976
- };
13977
-
13978
14042
  // Build new body content
13979
14043
  let newBodyContent = "";
13980
- let lastStatementCategory = null;
14044
+ let lastSortKey = null;
13981
14045
 
13982
14046
  for (let i = 0; i < sortedStatements.length; i++) {
13983
- const { category, statement, index } = sortedStatements[i];
14047
+ const { sortKey, statement } = sortedStatements[i];
13984
14048
 
13985
14049
  // Add blank line between different categories (except UNKNOWN)
13986
- if (lastStatementCategory !== null && category !== ORDER.UNKNOWN && lastStatementCategory !== ORDER.UNKNOWN && category !== lastStatementCategory) {
14050
+ if (lastSortKey !== null && sortKey !== ORDER.UNKNOWN && lastSortKey !== ORDER.UNKNOWN && sortKey !== lastSortKey) {
13987
14051
  newBodyContent += "\n";
13988
14052
  }
13989
14053
 
@@ -13992,12 +14056,12 @@ const reactCodeOrder = {
13992
14056
 
13993
14057
  newBodyContent += baseIndent + stmtText.trim() + "\n";
13994
14058
 
13995
- lastStatementCategory = category;
14059
+ lastSortKey = sortKey;
13996
14060
  }
13997
14061
 
13998
- // Find the range to replace
13999
- const firstStmt = relevantStatements[0];
14000
- const lastStmt = relevantStatements[relevantStatements.length - 1];
14062
+ // Find the range to replace (all statements)
14063
+ const firstStmt = statements[0];
14064
+ const lastStmt = statements[statements.length - 1];
14001
14065
 
14002
14066
  return fixer.replaceTextRange([firstStmt.range[0], lastStmt.range[1]], newBodyContent.trimEnd());
14003
14067
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
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",