eslint-plugin-code-style 1.0.16 → 1.0.17

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 (3) hide show
  1. package/README.md +16 -5
  2. package/index.js +191 -64
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -789,11 +789,11 @@ import { Button } from " @mui/material ";
789
789
 
790
790
  ### `index-export-style`
791
791
 
792
- Enforce consistent export style in index files. Choose between shorthand re-exports or import-then-export pattern.
792
+ Enforce consistent export style in index files. Choose between shorthand re-exports or import-then-export pattern. Also enforces no empty lines between exports/imports.
793
793
 
794
794
  **Style: "shorthand" (default)**
795
795
  ```javascript
796
- // Good - shorthand re-exports
796
+ // Good - shorthand re-exports (no empty lines between them)
797
797
  export { Button } from "./button";
798
798
  export { Input, Select } from "./form";
799
799
  export { StyledCard, StyledCardWithActions } from "./card";
@@ -801,7 +801,7 @@ export { StyledCard, StyledCardWithActions } from "./card";
801
801
 
802
802
  **Style: "import-export"**
803
803
  ```javascript
804
- // Good - import then export
804
+ // Good - imports grouped, single export statement at bottom
805
805
  import { Button } from "./button";
806
806
  import { Input, Select } from "./form";
807
807
  import { StyledCard, StyledCardWithActions } from "./card";
@@ -815,12 +815,23 @@ export {
815
815
  };
816
816
  ```
817
817
 
818
- **Bad - mixing styles**
818
+ **Bad Examples**
819
819
  ```javascript
820
- // Bad - don't mix shorthand with import-then-export
820
+ // Bad - mixing styles
821
821
  export { Button } from "./button";
822
822
  import { Input } from "./input";
823
823
  export { Input };
824
+
825
+ // Bad - empty lines between shorthand exports
826
+ export { Button } from "./button";
827
+
828
+ export { Input } from "./input";
829
+
830
+ // Bad - multiple standalone exports (should be one)
831
+ import { Button } from "./button";
832
+ import { Input } from "./input";
833
+ export { Button };
834
+ export { Input };
824
835
  ```
825
836
 
826
837
  **Customization Options:**
package/index.js CHANGED
@@ -3398,23 +3398,42 @@ const moduleIndexExports = {
3398
3398
  *
3399
3399
  * Options:
3400
3400
  * - style: "shorthand" (default) | "import-export"
3401
- * - "shorthand": export { a } from "./file";
3402
- * - "import-export": import { a } from "./file"; export { a };
3401
+ * - "shorthand": export { a } from "./file"; (no empty lines between exports)
3402
+ * - "import-export": import { a } from "./file"; export { a }; (single export statement)
3403
3403
  *
3404
3404
  * ✓ Good (style: "shorthand" - default):
3405
3405
  * export { Button } from "./button";
3406
3406
  * export { Input, Select } from "./form";
3407
+ * export { Modal } from "./modal";
3407
3408
  *
3408
3409
  * ✓ Good (style: "import-export"):
3409
3410
  * import { Button } from "./button";
3410
3411
  * import { Input, Select } from "./form";
3411
- * export { Button, Input, Select };
3412
+ * import { Modal } from "./modal";
3413
+ *
3414
+ * export {
3415
+ * Button,
3416
+ * Input,
3417
+ * Modal,
3418
+ * Select,
3419
+ * };
3412
3420
  *
3413
3421
  * ✗ Bad (mixing styles):
3414
3422
  * export { Button } from "./button";
3415
3423
  * import { Input } from "./input";
3416
3424
  * export { Input };
3417
3425
  *
3426
+ * ✗ Bad (empty lines between shorthand exports):
3427
+ * export { Button } from "./button";
3428
+ *
3429
+ * export { Input } from "./input";
3430
+ *
3431
+ * ✗ Bad (multiple standalone exports):
3432
+ * import { Button } from "./button";
3433
+ * import { Input } from "./input";
3434
+ * export { Button };
3435
+ * export { Input };
3436
+ *
3418
3437
  * Configuration Example:
3419
3438
  * "code-style/index-export-style": ["error", { style: "shorthand" }]
3420
3439
  * "code-style/index-export-style": ["error", { style: "import-export" }]
@@ -3484,24 +3503,28 @@ const indexExportStyle = {
3484
3503
 
3485
3504
  if (preferredStyle === "shorthand") {
3486
3505
  // Check if using import-then-export pattern when shorthand is preferred
3487
- standaloneExports.forEach((exportStmt) => {
3488
- const specifiersToConvert = [];
3489
-
3490
- exportStmt.specifiers.forEach((spec) => {
3491
- const exportedName = spec.local ? spec.local.name : spec.exported.name;
3492
- const importInfo = importSourceMap.get(exportedName);
3493
-
3494
- if (importInfo) {
3495
- specifiersToConvert.push({
3496
- exportedName,
3497
- importInfo,
3498
- localName: spec.local ? spec.local.name : exportedName,
3499
- spec,
3500
- });
3501
- }
3506
+ if (standaloneExports.length > 0 && imports.length > 0) {
3507
+ // Collect all specifiers to convert
3508
+ const allSpecifiersToConvert = [];
3509
+
3510
+ standaloneExports.forEach((exportStmt) => {
3511
+ exportStmt.specifiers.forEach((spec) => {
3512
+ const exportedName = spec.local ? spec.local.name : spec.exported.name;
3513
+ const importInfo = importSourceMap.get(exportedName);
3514
+
3515
+ if (importInfo) {
3516
+ allSpecifiersToConvert.push({
3517
+ exportStmt,
3518
+ exportedName,
3519
+ importInfo,
3520
+ localName: spec.local ? spec.local.name : exportedName,
3521
+ spec,
3522
+ });
3523
+ }
3524
+ });
3502
3525
  });
3503
3526
 
3504
- if (specifiersToConvert.length > 0) {
3527
+ if (allSpecifiersToConvert.length > 0) {
3505
3528
  context.report({
3506
3529
  fix(fixer) {
3507
3530
  const fixes = [];
@@ -3509,7 +3532,7 @@ const indexExportStyle = {
3509
3532
  // Group specifiers by source
3510
3533
  const bySource = new Map();
3511
3534
 
3512
- specifiersToConvert.forEach(({ exportedName, importInfo, localName }) => {
3535
+ allSpecifiersToConvert.forEach(({ exportedName, importInfo, localName }) => {
3513
3536
  const source = importInfo.source;
3514
3537
 
3515
3538
  if (!bySource.has(source)) {
@@ -3523,80 +3546,184 @@ const indexExportStyle = {
3523
3546
  }
3524
3547
  });
3525
3548
 
3526
- // Create shorthand exports
3549
+ // Create shorthand exports (no empty lines between them)
3527
3550
  const newExports = [];
3528
3551
 
3529
3552
  bySource.forEach((specifiers, source) => {
3530
3553
  newExports.push(`export { ${specifiers.join(", ")} } from "${source}";`);
3531
3554
  });
3532
3555
 
3533
- // Remove the standalone export
3534
- fixes.push(fixer.remove(exportStmt));
3535
-
3536
- // Remove associated imports
3537
- const importsToRemove = new Set();
3556
+ // Remove all standalone exports
3557
+ standaloneExports.forEach((exportStmt) => {
3558
+ fixes.push(fixer.remove(exportStmt));
3559
+ });
3538
3560
 
3539
- specifiersToConvert.forEach(({ importInfo }) => {
3540
- importsToRemove.add(importInfo.statement);
3561
+ // Remove all imports
3562
+ imports.forEach((importStmt) => {
3563
+ fixes.push(fixer.remove(importStmt));
3541
3564
  });
3542
3565
 
3543
- importsToRemove.forEach((importStmt) => {
3544
- // Check if all specifiers from this import are being converted
3545
- const allSpecifiersConverted = importStmt.specifiers.every((spec) => {
3546
- const name = spec.local ? spec.local.name : spec.imported.name;
3566
+ // Insert new shorthand exports at the beginning
3567
+ const firstStatement = node.body[0];
3547
3568
 
3548
- return specifiersToConvert.some((s) => s.localName === name);
3549
- });
3569
+ if (firstStatement) {
3570
+ fixes.push(fixer.insertTextBefore(firstStatement, newExports.join("\n") + "\n"));
3571
+ }
3550
3572
 
3551
- if (allSpecifiersConverted) {
3552
- fixes.push(fixer.remove(importStmt));
3553
- }
3554
- });
3573
+ return fixes;
3574
+ },
3575
+ message: `Use shorthand export style: export { ... } from "source" instead of import then export.`,
3576
+ node,
3577
+ });
3578
+ }
3579
+ }
3555
3580
 
3556
- // Insert new shorthand exports at the position of removed export
3557
- fixes.push(fixer.insertTextAfter(exportStmt, "\n" + newExports.join("\n")));
3581
+ // Check for empty lines between shorthand exports
3582
+ for (let i = 0; i < shorthandExports.length - 1; i += 1) {
3583
+ const currentExport = shorthandExports[i];
3584
+ const nextExport = shorthandExports[i + 1];
3585
+ const currentEndLine = currentExport.loc.end.line;
3586
+ const nextStartLine = nextExport.loc.start.line;
3558
3587
 
3559
- return fixes;
3588
+ if (nextStartLine - currentEndLine > 1) {
3589
+ context.report({
3590
+ fix(fixer) {
3591
+ const textBetween = sourceCode.getText().slice(
3592
+ currentExport.range[1],
3593
+ nextExport.range[0],
3594
+ );
3595
+
3596
+ // Replace multiple newlines with single newline
3597
+ return fixer.replaceTextRange(
3598
+ [currentExport.range[1], nextExport.range[0]],
3599
+ "\n",
3600
+ );
3560
3601
  },
3561
- message: `Use shorthand export: export { ... } from "source" instead of import then export.`,
3562
- node: exportStmt,
3602
+ message: "No empty lines between shorthand exports in index files.",
3603
+ node: nextExport,
3563
3604
  });
3564
3605
  }
3565
- });
3606
+ }
3566
3607
  } else if (preferredStyle === "import-export") {
3567
3608
  // Check if using shorthand when import-export is preferred
3568
- shorthandExports.forEach((exportStmt) => {
3569
- const source = exportStmt.source.value;
3570
- const specifiers = exportStmt.specifiers.map((spec) => {
3571
- const imported = spec.local ? spec.local.name : spec.exported.name;
3572
- const exported = spec.exported.name;
3573
-
3574
- if (imported === exported) {
3575
- return imported;
3576
- }
3609
+ if (shorthandExports.length > 0) {
3610
+ // Convert all shorthand exports to import-then-export with single export statement
3611
+ context.report({
3612
+ fix(fixer) {
3613
+ const fixes = [];
3614
+ const allImports = [];
3615
+ const allExportNames = [];
3616
+
3617
+ shorthandExports.forEach((exportStmt) => {
3618
+ const source = exportStmt.source.value;
3619
+ const importSpecifiers = [];
3577
3620
 
3578
- return `${imported} as ${exported}`;
3621
+ exportStmt.specifiers.forEach((spec) => {
3622
+ const imported = spec.local ? spec.local.name : spec.exported.name;
3623
+ const exported = spec.exported.name;
3624
+
3625
+ importSpecifiers.push(imported);
3626
+ allExportNames.push(exported);
3627
+ });
3628
+
3629
+ allImports.push(`import { ${importSpecifiers.join(", ")} } from "${source}";`);
3630
+
3631
+ // Remove the shorthand export
3632
+ fixes.push(fixer.remove(exportStmt));
3633
+ });
3634
+
3635
+ // Sort export names alphabetically
3636
+ allExportNames.sort((a, b) => a.localeCompare(b));
3637
+
3638
+ // Create single export statement with proper formatting
3639
+ let exportStatement;
3640
+
3641
+ if (allExportNames.length <= 3) {
3642
+ exportStatement = `export { ${allExportNames.join(", ")} };`;
3643
+ } else {
3644
+ exportStatement = `export {\n ${allExportNames.join(",\n ")},\n};`;
3645
+ }
3646
+
3647
+ // Insert imports and export at the beginning
3648
+ const firstStatement = node.body[0];
3649
+
3650
+ if (firstStatement) {
3651
+ const newContent = allImports.join("\n") + "\n\n" + exportStatement + "\n";
3652
+
3653
+ fixes.push(fixer.insertTextBefore(firstStatement, newContent));
3654
+ }
3655
+
3656
+ return fixes;
3657
+ },
3658
+ message: `Use import-then-export style with a single export statement.`,
3659
+ node,
3579
3660
  });
3661
+ }
3580
3662
 
3663
+ // Check for multiple standalone exports - should be combined into one
3664
+ if (standaloneExports.length > 1) {
3581
3665
  context.report({
3582
3666
  fix(fixer) {
3583
- const importSpecifiers = exportStmt.specifiers.map((spec) => {
3584
- const imported = spec.local ? spec.local.name : spec.exported.name;
3667
+ const fixes = [];
3668
+ const allExportNames = [];
3669
+
3670
+ standaloneExports.forEach((exportStmt) => {
3671
+ exportStmt.specifiers.forEach((spec) => {
3672
+ const exported = spec.exported.name;
3673
+
3674
+ allExportNames.push(exported);
3675
+ });
3585
3676
 
3586
- return imported;
3677
+ // Remove all but the last export
3678
+ fixes.push(fixer.remove(exportStmt));
3587
3679
  });
3588
3680
 
3589
- const exportSpecifiers = exportStmt.specifiers.map((spec) => spec.exported.name);
3681
+ // Sort export names alphabetically
3682
+ allExportNames.sort((a, b) => a.localeCompare(b));
3683
+
3684
+ // Create single export statement
3685
+ let exportStatement;
3686
+
3687
+ if (allExportNames.length <= 3) {
3688
+ exportStatement = `export { ${allExportNames.join(", ")} };`;
3689
+ } else {
3690
+ exportStatement = `export {\n ${allExportNames.join(",\n ")},\n};`;
3691
+ }
3692
+
3693
+ // Find last import to insert after
3694
+ const lastImport = imports[imports.length - 1];
3590
3695
 
3591
- const importLine = `import { ${importSpecifiers.join(", ")} } from "${source}";`;
3592
- const exportLine = `export { ${exportSpecifiers.join(", ")} };`;
3696
+ if (lastImport) {
3697
+ fixes.push(fixer.insertTextAfter(lastImport, "\n\n" + exportStatement));
3698
+ }
3593
3699
 
3594
- return fixer.replaceText(exportStmt, `${importLine}\n${exportLine}`);
3700
+ return fixes;
3595
3701
  },
3596
- message: `Use import-then-export style: import { ... } from "source"; export { ... };`,
3597
- node: exportStmt,
3702
+ message: `Combine multiple export statements into a single export statement.`,
3703
+ node,
3598
3704
  });
3599
- });
3705
+ }
3706
+
3707
+ // Check for empty lines between imports
3708
+ for (let i = 0; i < imports.length - 1; i += 1) {
3709
+ const currentImport = imports[i];
3710
+ const nextImport = imports[i + 1];
3711
+ const currentEndLine = currentImport.loc.end.line;
3712
+ const nextStartLine = nextImport.loc.start.line;
3713
+
3714
+ if (nextStartLine - currentEndLine > 1) {
3715
+ context.report({
3716
+ fix(fixer) {
3717
+ return fixer.replaceTextRange(
3718
+ [currentImport.range[1], nextImport.range[0]],
3719
+ "\n",
3720
+ );
3721
+ },
3722
+ message: "No empty lines between imports in index files.",
3723
+ node: nextImport,
3724
+ });
3725
+ }
3726
+ }
3600
3727
  }
3601
3728
  },
3602
3729
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-code-style",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
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",