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 CHANGED
@@ -6,7 +6,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/) principles.
6
6
 
7
7
  ---
8
8
 
9
- ## [1.4.0] - 2026-01-30
9
+ ## [1.4.2] - 2026-01-30
10
10
 
11
11
  **Release Title:** New Rules, Enhanced Auto-Fix & Comprehensive Documentation
12
12
 
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 type/interface definitions |
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 type or interface definitions.
5062
- * Types should be moved to a separate file (e.g., types.ts).
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
- TSInterfaceDeclaration(node) {
5085
- context.report({
5086
- message: "Interface definitions should not be in index files. Move to a types file.",
5087
- node,
5088
- });
5089
- },
5090
- TSTypeAliasDeclaration(node) {
5091
- context.report({
5092
- message: "Type definitions should not be in index files. Move to a types file.",
5093
- node,
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 type definitions" },
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 (current.type === "JSXAttribute"
6664
- || current.type === "VariableDeclarator"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
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",