eslint-plugin-code-style 1.4.3 → 1.4.5
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/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/index.js +142 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -298,7 +298,7 @@ rules: {
|
|
|
298
298
|
| `import-format` | `import {` and `} from` on same line; collapse ≤ threshold; expand larger with each specifier on own line (default: ≤3) 🔧 ⚙️ |
|
|
299
299
|
| `import-source-spacing` | No leading/trailing spaces inside import path quotes 🔧 |
|
|
300
300
|
| `index-export-style` | Index files: no blank lines, enforce shorthand or import-export style; Regular files: require blank lines between exports (default: shorthand) 🔧 ⚙️ |
|
|
301
|
-
| `index-exports-only` | Index files should only contain imports and exports, not
|
|
301
|
+
| `index-exports-only` | Index files should only contain imports and re-exports, not code definitions (types, functions, variables, classes) |
|
|
302
302
|
| `module-index-exports` | Index files must export all folder contents (files and subfolders) ⚙️ |
|
|
303
303
|
| **JSX Rules** | |
|
|
304
304
|
| `classname-dynamic-at-end` | Dynamic expressions (`${className}`) must be at the end of class strings (JSX and variables) 🔧 |
|
package/index.js
CHANGED
|
@@ -5058,18 +5058,23 @@ const indexExportStyle = {
|
|
|
5058
5058
|
*
|
|
5059
5059
|
* Description:
|
|
5060
5060
|
* Index files (index.ts, index.tsx, index.js, index.jsx) should
|
|
5061
|
-
* only contain imports and exports, not
|
|
5062
|
-
*
|
|
5061
|
+
* only contain imports and re-exports, not any code definitions.
|
|
5062
|
+
* All definitions (types, interfaces, functions, variables, classes)
|
|
5063
|
+
* should be moved to separate files.
|
|
5063
5064
|
*
|
|
5064
5065
|
* ✓ Good:
|
|
5065
5066
|
* // index.ts
|
|
5066
5067
|
* export { Button } from "./Button";
|
|
5068
|
+
* export { helper } from "./utils";
|
|
5067
5069
|
* export type { ButtonProps } from "./types";
|
|
5070
|
+
* export * from "./constants";
|
|
5068
5071
|
*
|
|
5069
5072
|
* ✗ Bad:
|
|
5070
5073
|
* // index.ts
|
|
5071
5074
|
* export type ButtonVariant = "primary" | "secondary";
|
|
5072
5075
|
* export interface ButtonProps { ... }
|
|
5076
|
+
* export const CONSTANT = "value";
|
|
5077
|
+
* export function helper() { ... }
|
|
5073
5078
|
*/
|
|
5074
5079
|
const indexExportsOnly = {
|
|
5075
5080
|
create(context) {
|
|
@@ -5080,23 +5085,76 @@ const indexExportsOnly = {
|
|
|
5080
5085
|
|
|
5081
5086
|
if (!isIndexFile) return {};
|
|
5082
5087
|
|
|
5088
|
+
// Helper to check if a node is an import or export statement
|
|
5089
|
+
const isImportOrExportHandler = (node) => {
|
|
5090
|
+
const { type } = node;
|
|
5091
|
+
|
|
5092
|
+
// Import statements
|
|
5093
|
+
if (type === "ImportDeclaration") return true;
|
|
5094
|
+
|
|
5095
|
+
// Export statements (named, default, all)
|
|
5096
|
+
if (type === "ExportNamedDeclaration") {
|
|
5097
|
+
// Only allow re-exports (export { x } from "./module" or export { x })
|
|
5098
|
+
// If it has a declaration, it's defining something (not allowed)
|
|
5099
|
+
return !node.declaration;
|
|
5100
|
+
}
|
|
5101
|
+
|
|
5102
|
+
if (type === "ExportDefaultDeclaration") {
|
|
5103
|
+
// Allow export default <identifier> (re-exporting)
|
|
5104
|
+
// Disallow export default function/class/object (defining something)
|
|
5105
|
+
return node.declaration && node.declaration.type === "Identifier";
|
|
5106
|
+
}
|
|
5107
|
+
|
|
5108
|
+
if (type === "ExportAllDeclaration") return true;
|
|
5109
|
+
|
|
5110
|
+
return false;
|
|
5111
|
+
};
|
|
5112
|
+
|
|
5113
|
+
// Get a friendly description of what the disallowed node is
|
|
5114
|
+
const getNodeDescriptionHandler = (node) => {
|
|
5115
|
+
switch (node.type) {
|
|
5116
|
+
case "TSTypeAliasDeclaration":
|
|
5117
|
+
return "Type definition";
|
|
5118
|
+
case "TSInterfaceDeclaration":
|
|
5119
|
+
return "Interface definition";
|
|
5120
|
+
case "TSEnumDeclaration":
|
|
5121
|
+
return "Enum definition";
|
|
5122
|
+
case "VariableDeclaration":
|
|
5123
|
+
return "Variable declaration";
|
|
5124
|
+
case "FunctionDeclaration":
|
|
5125
|
+
return "Function declaration";
|
|
5126
|
+
case "ClassDeclaration":
|
|
5127
|
+
return "Class declaration";
|
|
5128
|
+
case "ExportNamedDeclaration":
|
|
5129
|
+
if (node.declaration) {
|
|
5130
|
+
return getNodeDescriptionHandler(node.declaration);
|
|
5131
|
+
}
|
|
5132
|
+
|
|
5133
|
+
return "Export with inline declaration";
|
|
5134
|
+
case "ExportDefaultDeclaration":
|
|
5135
|
+
return "Default export with inline definition";
|
|
5136
|
+
default:
|
|
5137
|
+
return "Code";
|
|
5138
|
+
}
|
|
5139
|
+
};
|
|
5140
|
+
|
|
5083
5141
|
return {
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
}
|
|
5142
|
+
Program(programNode) {
|
|
5143
|
+
for (const node of programNode.body) {
|
|
5144
|
+
if (!isImportOrExportHandler(node)) {
|
|
5145
|
+
const description = getNodeDescriptionHandler(node);
|
|
5146
|
+
|
|
5147
|
+
context.report({
|
|
5148
|
+
message: `${description} should not be in index files. Index files should only contain imports and re-exports. Move this to a separate file.`,
|
|
5149
|
+
node,
|
|
5150
|
+
});
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5095
5153
|
},
|
|
5096
5154
|
};
|
|
5097
5155
|
},
|
|
5098
5156
|
meta: {
|
|
5099
|
-
docs: { description: "Index files should only contain imports and exports, not
|
|
5157
|
+
docs: { description: "Index files should only contain imports and re-exports, not code definitions" },
|
|
5100
5158
|
schema: [],
|
|
5101
5159
|
type: "suggestion",
|
|
5102
5160
|
},
|
|
@@ -6660,24 +6718,25 @@ const classNameMultiline = {
|
|
|
6660
6718
|
let current = node;
|
|
6661
6719
|
|
|
6662
6720
|
while (current) {
|
|
6663
|
-
if
|
|
6664
|
-
|
|
6665
|
-
|| current.type === "Property") {
|
|
6721
|
+
// For JSX attributes, check if inline and use column-based indent
|
|
6722
|
+
if (current.type === "JSXAttribute") {
|
|
6666
6723
|
const lineIndent = getLineIndent(current);
|
|
6667
6724
|
const lineText = sourceCode.lines[current.loc.start.line - 1];
|
|
6668
|
-
|
|
6669
|
-
// Check if there's content before the node on this line
|
|
6670
6725
|
const contentBefore = lineText.slice(0, current.loc.start.column).trim();
|
|
6671
6726
|
|
|
6672
6727
|
if (contentBefore) {
|
|
6673
6728
|
// Attribute is inline (e.g., <Component className=...>)
|
|
6674
|
-
// Use column position as indent for proper alignment
|
|
6675
6729
|
return " ".repeat(current.loc.start.column);
|
|
6676
6730
|
}
|
|
6677
6731
|
|
|
6678
6732
|
return lineIndent;
|
|
6679
6733
|
}
|
|
6680
6734
|
|
|
6735
|
+
// For variables and properties, just use line indentation
|
|
6736
|
+
if (current.type === "VariableDeclarator" || current.type === "Property") {
|
|
6737
|
+
return getLineIndent(current);
|
|
6738
|
+
}
|
|
6739
|
+
|
|
6681
6740
|
current = current.parent;
|
|
6682
6741
|
}
|
|
6683
6742
|
|
|
@@ -12215,6 +12274,38 @@ const functionObjectDestructure = {
|
|
|
12215
12274
|
return destructured;
|
|
12216
12275
|
};
|
|
12217
12276
|
|
|
12277
|
+
// Find the destructuring statement and property node for a variable
|
|
12278
|
+
const findDestructuringStatementHandler = (blockBody, varName, paramName) => {
|
|
12279
|
+
if (blockBody.type !== "BlockStatement") return null;
|
|
12280
|
+
|
|
12281
|
+
for (const stmt of blockBody.body) {
|
|
12282
|
+
if (stmt.type !== "VariableDeclaration") continue;
|
|
12283
|
+
|
|
12284
|
+
for (const decl of stmt.declarations) {
|
|
12285
|
+
if (decl.id.type === "ObjectPattern" && decl.init) {
|
|
12286
|
+
// Check if this destructuring creates the variable we're looking for
|
|
12287
|
+
if (decl.init.type === "Identifier" && decl.init.name === paramName) {
|
|
12288
|
+
for (const prop of decl.id.properties) {
|
|
12289
|
+
if (prop.type === "Property" && prop.key.type === "Identifier") {
|
|
12290
|
+
const createdVarName = prop.value.type === "Identifier" ? prop.value.name : prop.key.name;
|
|
12291
|
+
|
|
12292
|
+
if (createdVarName === varName) {
|
|
12293
|
+
return {
|
|
12294
|
+
declarator: decl,
|
|
12295
|
+
property: prop,
|
|
12296
|
+
statement: stmt,
|
|
12297
|
+
};
|
|
12298
|
+
}
|
|
12299
|
+
}
|
|
12300
|
+
}
|
|
12301
|
+
}
|
|
12302
|
+
}
|
|
12303
|
+
}
|
|
12304
|
+
}
|
|
12305
|
+
|
|
12306
|
+
return null;
|
|
12307
|
+
};
|
|
12308
|
+
|
|
12218
12309
|
const checkFunctionHandler = (node) => {
|
|
12219
12310
|
const isComponent = isReactComponentHandler(node);
|
|
12220
12311
|
const params = node.params;
|
|
@@ -12327,7 +12418,29 @@ const functionObjectDestructure = {
|
|
|
12327
12418
|
if (accesses.length > 0) {
|
|
12328
12419
|
const accessedProps = [...new Set(accesses.map((a) => a.property))];
|
|
12329
12420
|
|
|
12421
|
+
// Find the original destructuring statement
|
|
12422
|
+
const destructInfo = findDestructuringStatementHandler(body, varName, firstParam.name);
|
|
12423
|
+
|
|
12330
12424
|
context.report({
|
|
12425
|
+
fix: destructInfo
|
|
12426
|
+
? (fixer) => {
|
|
12427
|
+
const fixes = [];
|
|
12428
|
+
|
|
12429
|
+
// Modify the destructuring property to use nested destructuring
|
|
12430
|
+
// e.g., { target } becomes { target: { value } }
|
|
12431
|
+
const { property } = destructInfo;
|
|
12432
|
+
const nestedDestructure = `${info.originalProp}: { ${accessedProps.join(", ")} }`;
|
|
12433
|
+
|
|
12434
|
+
fixes.push(fixer.replaceText(property, nestedDestructure));
|
|
12435
|
+
|
|
12436
|
+
// Replace all varName.prop accesses with just prop
|
|
12437
|
+
accesses.forEach((access) => {
|
|
12438
|
+
fixes.push(fixer.replaceText(access.node, access.property));
|
|
12439
|
+
});
|
|
12440
|
+
|
|
12441
|
+
return fixes;
|
|
12442
|
+
}
|
|
12443
|
+
: undefined,
|
|
12331
12444
|
message: `Variable "${varName}" is accessed via dot notation (${accessedProps.join(", ")}). Use nested destructuring instead: "const { ${info.originalProp}: { ${accessedProps.join(", ")} } } = ..."`,
|
|
12332
12445
|
node: accesses[0].node,
|
|
12333
12446
|
});
|
|
@@ -13138,6 +13251,15 @@ const noInlineTypeDefinitions = {
|
|
|
13138
13251
|
return;
|
|
13139
13252
|
}
|
|
13140
13253
|
|
|
13254
|
+
// Handle intersection types (e.g., ButtonHTMLAttributes & { variant: "a" | "b" })
|
|
13255
|
+
if (typeNode.type === "TSIntersectionType") {
|
|
13256
|
+
for (const type of typeNode.types) {
|
|
13257
|
+
checkTypeAnnotationHandler(type, paramName);
|
|
13258
|
+
}
|
|
13259
|
+
|
|
13260
|
+
return;
|
|
13261
|
+
}
|
|
13262
|
+
|
|
13141
13263
|
// Handle object types with union properties
|
|
13142
13264
|
if (typeNode.type === "TSTypeLiteral") {
|
|
13143
13265
|
for (const member of typeNode.members) {
|
package/package.json
CHANGED