eslint-plugin-code-style 1.3.8 → 1.3.9

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 +100 -19
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -13846,6 +13846,7 @@ const reactCodeOrder = {
13846
13846
 
13847
13847
  const checkCodeOrderHandler = (node, isHook) => {
13848
13848
  const body = node.body;
13849
+ const sourceCode = context.sourceCode || context.getSourceCode();
13849
13850
 
13850
13851
  // Only check block statements (not implicit returns)
13851
13852
  if (body.type !== "BlockStatement") return;
@@ -13902,36 +13903,115 @@ const reactCodeOrder = {
13902
13903
 
13903
13904
  if (relevantStatements.length < 2) return;
13904
13905
 
13905
- // Track the categories and their order
13906
+ // Collect all statements with their categories and original positions
13907
+ const statementsWithCategories = [];
13908
+ let hasOrderViolation = false;
13906
13909
  let lastCategory = 0;
13907
- let lastCategoryStatement = null;
13910
+ let violatingStatement = null;
13911
+ let violatingCategory = null;
13912
+ let previousCategory = null;
13908
13913
 
13909
- for (const statement of relevantStatements) {
13914
+ for (let i = 0; i < relevantStatements.length; i++) {
13915
+ const statement = relevantStatements[i];
13910
13916
  const category = getStatementCategoryHandler(statement, propNames);
13911
13917
 
13912
- // Skip unknown statements - they could be anything
13918
+ statementsWithCategories.push({
13919
+ category,
13920
+ index: i,
13921
+ statement,
13922
+ });
13923
+
13924
+ // Skip unknown statements for order checking
13913
13925
  if (category === ORDER.UNKNOWN) continue;
13914
13926
 
13915
13927
  // Check if current category comes before the last one
13916
- if (category < lastCategory) {
13917
- // Find what it should come after
13918
- context.report({
13919
- data: {
13920
- current: ORDER_NAMES[category],
13921
- previous: ORDER_NAMES[lastCategory],
13922
- type: isHook ? "hook" : "component",
13923
- },
13924
- message: "\"{{current}}\" should come before \"{{previous}}\" in {{type}}. Order: refs → state → redux → router → context → custom hooks → derived → useMemo → useCallback → handlers → useEffect → return",
13925
- node: statement,
13926
- });
13927
-
13928
- // Only report first violation to avoid noise
13929
- return;
13928
+ if (category < lastCategory && !hasOrderViolation) {
13929
+ hasOrderViolation = true;
13930
+ violatingStatement = statement;
13931
+ violatingCategory = category;
13932
+ previousCategory = lastCategory;
13930
13933
  }
13931
13934
 
13932
13935
  lastCategory = category;
13933
- lastCategoryStatement = statement;
13934
13936
  }
13937
+
13938
+ if (!hasOrderViolation) return;
13939
+
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;
13944
+ }
13945
+
13946
+ // Maintain original order within the same category
13947
+ return a.index - b.index;
13948
+ });
13949
+
13950
+ // Check if sorting actually changes the order
13951
+ const orderChanged = sortedStatements.some((s, i) => s.index !== statementsWithCategories[i].index);
13952
+
13953
+ if (!orderChanged) return;
13954
+
13955
+ // Build the fix
13956
+ const fixHandler = (fixer) => {
13957
+ // Get the base indentation from the first statement
13958
+ const firstStatementLine = sourceCode.lines[relevantStatements[0].loc.start.line - 1];
13959
+ const baseIndent = firstStatementLine.match(/^\s*/)[0];
13960
+
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
+ // Build new body content
13979
+ let newBodyContent = "";
13980
+ let lastStatementCategory = null;
13981
+
13982
+ for (let i = 0; i < sortedStatements.length; i++) {
13983
+ const { category, statement, index } = sortedStatements[i];
13984
+
13985
+ // Add blank line between different categories (except UNKNOWN)
13986
+ if (lastStatementCategory !== null && category !== ORDER.UNKNOWN && lastStatementCategory !== ORDER.UNKNOWN && category !== lastStatementCategory) {
13987
+ newBodyContent += "\n";
13988
+ }
13989
+
13990
+ // Get the statement text with proper indentation
13991
+ const stmtText = sourceCode.getText(statement);
13992
+
13993
+ newBodyContent += baseIndent + stmtText.trim() + "\n";
13994
+
13995
+ lastStatementCategory = category;
13996
+ }
13997
+
13998
+ // Find the range to replace
13999
+ const firstStmt = relevantStatements[0];
14000
+ const lastStmt = relevantStatements[relevantStatements.length - 1];
14001
+
14002
+ return fixer.replaceTextRange([firstStmt.range[0], lastStmt.range[1]], newBodyContent.trimEnd());
14003
+ };
14004
+
14005
+ context.report({
14006
+ data: {
14007
+ current: ORDER_NAMES[violatingCategory],
14008
+ previous: ORDER_NAMES[previousCategory],
14009
+ type: isHook ? "hook" : "component",
14010
+ },
14011
+ fix: fixHandler,
14012
+ message: "\"{{current}}\" should come before \"{{previous}}\" in {{type}}. Order: refs → state → redux → router → context → custom hooks → derived → useMemo → useCallback → handlers → useEffect → return",
14013
+ node: violatingStatement,
14014
+ });
13935
14015
  };
13936
14016
 
13937
14017
  const checkFunctionHandler = (node) => {
@@ -13956,6 +14036,7 @@ const reactCodeOrder = {
13956
14036
  },
13957
14037
  meta: {
13958
14038
  docs: { description: "Enforce consistent ordering of code blocks in React components and custom hooks" },
14039
+ fixable: "code",
13959
14040
  schema: [],
13960
14041
  type: "suggestion",
13961
14042
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
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",