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.
- package/index.js +110 -46
- 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
|
-
//
|
|
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
|
-
//
|
|
13899
|
-
const
|
|
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 (
|
|
13951
|
+
if (categorizableStatements.length < 2) return;
|
|
13905
13952
|
|
|
13906
|
-
//
|
|
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 (
|
|
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
|
-
//
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
|
|
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
|
-
|
|
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 !==
|
|
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[
|
|
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
|
|
14044
|
+
let lastSortKey = null;
|
|
13981
14045
|
|
|
13982
14046
|
for (let i = 0; i < sortedStatements.length; i++) {
|
|
13983
|
-
const {
|
|
14047
|
+
const { sortKey, statement } = sortedStatements[i];
|
|
13984
14048
|
|
|
13985
14049
|
// Add blank line between different categories (except UNKNOWN)
|
|
13986
|
-
if (
|
|
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
|
-
|
|
14059
|
+
lastSortKey = sortKey;
|
|
13996
14060
|
}
|
|
13997
14061
|
|
|
13998
|
-
// Find the range to replace
|
|
13999
|
-
const firstStmt =
|
|
14000
|
-
const lastStmt =
|
|
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