eslint-plugin-code-style 1.6.0 → 1.6.4
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 +47 -2
- package/index.js +221 -78
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,11 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.6.4] - 2026-02-01
|
|
11
|
+
|
|
12
|
+
### Enhanced
|
|
13
|
+
|
|
14
|
+
- **`function-object-destructure`** - Add more module paths: apis, configs, utilities, routes
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## [1.6.3] - 2026-02-01
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **`component-props-destructure`** - Preserve TypeScript type annotation when auto-fixing
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## [1.6.2] - 2026-02-01
|
|
27
|
+
|
|
28
|
+
### Enhanced
|
|
29
|
+
|
|
30
|
+
- **`function-object-destructure`** - Expand to check more module paths (services, constants, config, api, utils, helpers, lib) for dot notation enforcement
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## [1.6.1] - 2026-02-01
|
|
35
|
+
|
|
36
|
+
### Enhanced
|
|
37
|
+
|
|
38
|
+
- **`function-params-per-line`** - Handle callbacks with mixed params (destructured + simple like `({ item }, index)`)
|
|
39
|
+
- **`array-callback-destructure`** - Fix closing brace on same line as last property
|
|
40
|
+
- **`simple-call-single-line`** - Skip callbacks with 2+ params to avoid conflicts
|
|
41
|
+
- **`jsx-simple-element-one-line`** - Treat simple function calls (0-1 args) as simple expressions
|
|
42
|
+
- **`jsx-children-on-new-line`** - Treat simple function calls (0-1 args) as simple expressions
|
|
43
|
+
- **`jsx-element-child-new-line`** - Treat simple function calls (0-1 args) as simple expressions
|
|
44
|
+
|
|
45
|
+
### Docs
|
|
46
|
+
|
|
47
|
+
- Clarify version bump and tag workflow in AGENTS.md
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
10
51
|
## [1.6.0] - 2026-02-01
|
|
11
52
|
|
|
12
53
|
**New array-callback-destructure Rule & Multiple Enhancements**
|
|
13
54
|
|
|
14
|
-
**Version Range:** v1.5.
|
|
55
|
+
**Version Range:** v1.5.1 → v1.6.0
|
|
15
56
|
|
|
16
57
|
### Added
|
|
17
58
|
|
|
@@ -33,7 +74,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
33
74
|
- Auto-fixable: 60 rules 🔧
|
|
34
75
|
- Report-only: 6 rules
|
|
35
76
|
|
|
36
|
-
**Full Changelog:** [v1.5.
|
|
77
|
+
**Full Changelog:** [v1.5.1...v1.6.0](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.5.1...v1.6.0)
|
|
37
78
|
|
|
38
79
|
---
|
|
39
80
|
|
|
@@ -970,6 +1011,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
970
1011
|
|
|
971
1012
|
---
|
|
972
1013
|
|
|
1014
|
+
[1.6.4]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.6.3...v1.6.4
|
|
1015
|
+
[1.6.3]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.6.2...v1.6.3
|
|
1016
|
+
[1.6.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.6.1...v1.6.2
|
|
1017
|
+
[1.6.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.6.0...v1.6.1
|
|
973
1018
|
[1.6.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.5.2...v1.6.0
|
|
974
1019
|
[1.5.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.5.1...v1.5.2
|
|
975
1020
|
[1.5.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.5.0...v1.5.1
|
package/index.js
CHANGED
|
@@ -585,20 +585,20 @@ const arrayCallbackDestructure = {
|
|
|
585
585
|
// Only enforce multiline when 2+ properties
|
|
586
586
|
if (properties.length < 2) return;
|
|
587
587
|
|
|
588
|
-
// Check if all properties are on the same line
|
|
589
588
|
const firstProp = properties[0];
|
|
590
589
|
const lastProp = properties[properties.length - 1];
|
|
590
|
+
const closeBrace = sourceCode.getLastToken(pattern);
|
|
591
591
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const propIndent = baseIndent + " ";
|
|
592
|
+
// Calculate indent based on the call expression
|
|
593
|
+
const callLine = sourceCode.lines[callNode.loc.start.line - 1];
|
|
594
|
+
const baseIndent = callLine.match(/^\s*/)[0];
|
|
595
|
+
const propIndent = baseIndent + " ";
|
|
597
596
|
|
|
597
|
+
// Check if all properties are on the same line (need full reformat)
|
|
598
|
+
if (firstProp.loc.start.line === lastProp.loc.end.line) {
|
|
598
599
|
context.report({
|
|
599
600
|
fix(fixer) {
|
|
600
601
|
const openBrace = sourceCode.getFirstToken(pattern);
|
|
601
|
-
const closeBrace = sourceCode.getLastToken(pattern);
|
|
602
602
|
|
|
603
603
|
const propsText = properties
|
|
604
604
|
.map((prop) => propIndent + sourceCode.getText(prop))
|
|
@@ -606,12 +606,39 @@ const arrayCallbackDestructure = {
|
|
|
606
606
|
|
|
607
607
|
return fixer.replaceTextRange(
|
|
608
608
|
[openBrace.range[0], closeBrace.range[1]],
|
|
609
|
-
`{\n${propsText},\n${baseIndent}}`,
|
|
609
|
+
`{\n${propsText},\n${baseIndent} }`,
|
|
610
610
|
);
|
|
611
611
|
},
|
|
612
612
|
message: "Destructured properties in array callback should each be on their own line when there are 2 or more properties",
|
|
613
613
|
node: pattern,
|
|
614
614
|
});
|
|
615
|
+
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Check if closing brace is on the same line as last property (needs fixing)
|
|
620
|
+
if (closeBrace.loc.start.line === lastProp.loc.end.line) {
|
|
621
|
+
context.report({
|
|
622
|
+
fix(fixer) {
|
|
623
|
+
// Add trailing comma if missing and move closing brace to new line
|
|
624
|
+
const tokenBeforeClose = sourceCode.getTokenBefore(closeBrace);
|
|
625
|
+
const hasTrailingComma = tokenBeforeClose.value === ",";
|
|
626
|
+
|
|
627
|
+
if (hasTrailingComma) {
|
|
628
|
+
return fixer.replaceTextRange(
|
|
629
|
+
[tokenBeforeClose.range[1], closeBrace.range[0]],
|
|
630
|
+
"\n" + baseIndent + " ",
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return fixer.replaceTextRange(
|
|
635
|
+
[lastProp.range[1], closeBrace.range[0]],
|
|
636
|
+
",\n" + baseIndent + " ",
|
|
637
|
+
);
|
|
638
|
+
},
|
|
639
|
+
message: "Closing brace should be on its own line in multiline destructuring",
|
|
640
|
+
node: closeBrace,
|
|
641
|
+
});
|
|
615
642
|
}
|
|
616
643
|
};
|
|
617
644
|
|
|
@@ -2694,67 +2721,66 @@ const functionParamsPerLine = {
|
|
|
2694
2721
|
closeParen.range[0],
|
|
2695
2722
|
);
|
|
2696
2723
|
|
|
2697
|
-
// Callback arrow functions with 2+
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2724
|
+
// Callback arrow functions with 2+ params: each param on its own line
|
|
2725
|
+
// This handles both simple params (item, index) and mixed params ({ item }, index)
|
|
2726
|
+
if (isCallback && params.length >= 2) {
|
|
2727
|
+
// Calculate proper indent based on line's base indentation, not the paren column
|
|
2728
|
+
// This ensures consistent indentation for callbacks in method chains
|
|
2729
|
+
const lineText = sourceCode.lines[openParen.loc.start.line - 1];
|
|
2730
|
+
const lineIndent = lineText.match(/^(\s*)/)[1].length;
|
|
2731
|
+
const paramIndent = " ".repeat(lineIndent + 4);
|
|
2732
|
+
const parenIndent = " ".repeat(lineIndent);
|
|
2733
|
+
|
|
2734
|
+
// Check if first param is on same line as opening paren
|
|
2735
|
+
if (openParen.loc.end.line === firstParam.loc.start.line) {
|
|
2736
|
+
context.report({
|
|
2737
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
2738
|
+
[openParen.range[1], firstParam.range[0]],
|
|
2739
|
+
"\n" + paramIndent,
|
|
2740
|
+
),
|
|
2741
|
+
message: "Callback arrow with 2+ params: first param should be on its own line",
|
|
2742
|
+
node: firstParam,
|
|
2743
|
+
});
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
// Check if closing paren is on same line as last param
|
|
2747
|
+
if (closeParen.loc.start.line === lastParam.loc.end.line) {
|
|
2748
|
+
context.report({
|
|
2749
|
+
fix: (fixer) => fixer.replaceTextRange(
|
|
2750
|
+
[lastParam.range[1], closeParen.range[0]],
|
|
2751
|
+
",\n" + parenIndent,
|
|
2752
|
+
),
|
|
2753
|
+
message: "Callback arrow with 2+ params: closing paren should be on its own line",
|
|
2754
|
+
node: closeParen,
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
// Check each param is on its own line
|
|
2759
|
+
for (let i = 0; i < params.length - 1; i += 1) {
|
|
2760
|
+
const current = params[i];
|
|
2761
|
+
const next = params[i + 1];
|
|
2701
2762
|
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2763
|
+
if (current.loc.end.line === next.loc.start.line) {
|
|
2764
|
+
const commaToken = sourceCode.getTokenAfter(
|
|
2765
|
+
current,
|
|
2766
|
+
(token) => token.value === ",",
|
|
2767
|
+
);
|
|
2706
2768
|
|
|
2707
|
-
// Check if first param is on same line as opening paren
|
|
2708
|
-
if (openParen.loc.end.line === firstParam.loc.start.line) {
|
|
2709
2769
|
context.report({
|
|
2710
2770
|
fix: (fixer) => fixer.replaceTextRange(
|
|
2711
|
-
[
|
|
2771
|
+
[commaToken.range[1], next.range[0]],
|
|
2712
2772
|
"\n" + paramIndent,
|
|
2713
2773
|
),
|
|
2714
|
-
message: "Callback arrow with 2+ params:
|
|
2715
|
-
node:
|
|
2716
|
-
});
|
|
2717
|
-
}
|
|
2718
|
-
|
|
2719
|
-
// Check if closing paren is on same line as last param
|
|
2720
|
-
if (closeParen.loc.start.line === lastParam.loc.end.line) {
|
|
2721
|
-
context.report({
|
|
2722
|
-
fix: (fixer) => fixer.replaceTextRange(
|
|
2723
|
-
[lastParam.range[1], closeParen.range[0]],
|
|
2724
|
-
",\n" + parenIndent,
|
|
2725
|
-
),
|
|
2726
|
-
message: "Callback arrow with 2+ params: closing paren should be on its own line",
|
|
2727
|
-
node: closeParen,
|
|
2774
|
+
message: "Callback arrow with 2+ params: each param should be on its own line",
|
|
2775
|
+
node: next,
|
|
2728
2776
|
});
|
|
2729
2777
|
}
|
|
2730
|
-
|
|
2731
|
-
// Check each param is on its own line
|
|
2732
|
-
for (let i = 0; i < params.length - 1; i += 1) {
|
|
2733
|
-
const current = params[i];
|
|
2734
|
-
const next = params[i + 1];
|
|
2735
|
-
|
|
2736
|
-
if (current.loc.end.line === next.loc.start.line) {
|
|
2737
|
-
const commaToken = sourceCode.getTokenAfter(
|
|
2738
|
-
current,
|
|
2739
|
-
(token) => token.value === ",",
|
|
2740
|
-
);
|
|
2741
|
-
|
|
2742
|
-
context.report({
|
|
2743
|
-
fix: (fixer) => fixer.replaceTextRange(
|
|
2744
|
-
[commaToken.range[1], next.range[0]],
|
|
2745
|
-
"\n" + paramIndent,
|
|
2746
|
-
),
|
|
2747
|
-
message: "Callback arrow with 2+ params: each param should be on its own line",
|
|
2748
|
-
node: next,
|
|
2749
|
-
});
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
|
-
return;
|
|
2754
2778
|
}
|
|
2779
|
+
|
|
2780
|
+
return;
|
|
2755
2781
|
}
|
|
2756
2782
|
|
|
2757
|
-
// Skip
|
|
2783
|
+
// Skip single-param callback arrow functions (handled by opening-brackets-same-line)
|
|
2758
2784
|
if (isCallback) return;
|
|
2759
2785
|
|
|
2760
2786
|
// 1-2 simple params without complex destructuring: keep on same line
|
|
@@ -5651,7 +5677,19 @@ const jsxChildrenOnNewLine = {
|
|
|
5651
5677
|
create(context) {
|
|
5652
5678
|
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
5653
5679
|
|
|
5654
|
-
// Check if
|
|
5680
|
+
// Check if an argument is simple
|
|
5681
|
+
const isSimpleArgHandler = (node) => {
|
|
5682
|
+
if (!node) return false;
|
|
5683
|
+
if (node.type === "Identifier") return true;
|
|
5684
|
+
if (node.type === "Literal") return true;
|
|
5685
|
+
if (node.type === "MemberExpression") {
|
|
5686
|
+
return node.object.type === "Identifier" && node.property.type === "Identifier";
|
|
5687
|
+
}
|
|
5688
|
+
|
|
5689
|
+
return false;
|
|
5690
|
+
};
|
|
5691
|
+
|
|
5692
|
+
// Check if expression is simple (identifier, literal, member expression chain, or simple function call)
|
|
5655
5693
|
const isSimpleExpressionHandler = (expr) => {
|
|
5656
5694
|
if (!expr) return true;
|
|
5657
5695
|
|
|
@@ -5677,6 +5715,22 @@ const jsxChildrenOnNewLine = {
|
|
|
5677
5715
|
return current.type === "Identifier";
|
|
5678
5716
|
}
|
|
5679
5717
|
|
|
5718
|
+
// Allow simple function calls with 0-1 simple arguments
|
|
5719
|
+
if (expr.type === "CallExpression") {
|
|
5720
|
+
const { callee } = expr;
|
|
5721
|
+
const isSimpleCallee = callee.type === "Identifier" ||
|
|
5722
|
+
(callee.type === "MemberExpression" &&
|
|
5723
|
+
callee.object.type === "Identifier" &&
|
|
5724
|
+
callee.property.type === "Identifier");
|
|
5725
|
+
|
|
5726
|
+
if (!isSimpleCallee) return false;
|
|
5727
|
+
|
|
5728
|
+
if (expr.arguments.length === 0) return true;
|
|
5729
|
+
if (expr.arguments.length === 1 && isSimpleArgHandler(expr.arguments[0])) return true;
|
|
5730
|
+
|
|
5731
|
+
return false;
|
|
5732
|
+
}
|
|
5733
|
+
|
|
5680
5734
|
return false;
|
|
5681
5735
|
};
|
|
5682
5736
|
|
|
@@ -5884,7 +5938,19 @@ const jsxElementChildNewLine = {
|
|
|
5884
5938
|
create(context) {
|
|
5885
5939
|
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
5886
5940
|
|
|
5887
|
-
// Check if
|
|
5941
|
+
// Check if an argument is simple
|
|
5942
|
+
const isSimpleArgHandler = (node) => {
|
|
5943
|
+
if (!node) return false;
|
|
5944
|
+
if (node.type === "Identifier") return true;
|
|
5945
|
+
if (node.type === "Literal") return true;
|
|
5946
|
+
if (node.type === "MemberExpression") {
|
|
5947
|
+
return node.object.type === "Identifier" && node.property.type === "Identifier";
|
|
5948
|
+
}
|
|
5949
|
+
|
|
5950
|
+
return false;
|
|
5951
|
+
};
|
|
5952
|
+
|
|
5953
|
+
// Check if expression is simple (identifier, literal, member expression chain, or simple function call)
|
|
5888
5954
|
const isSimpleExpressionHandler = (expr) => {
|
|
5889
5955
|
if (!expr) return true;
|
|
5890
5956
|
|
|
@@ -5910,6 +5976,22 @@ const jsxElementChildNewLine = {
|
|
|
5910
5976
|
return current.type === "Identifier";
|
|
5911
5977
|
}
|
|
5912
5978
|
|
|
5979
|
+
// Allow simple function calls with 0-1 simple arguments
|
|
5980
|
+
if (expr.type === "CallExpression") {
|
|
5981
|
+
const { callee } = expr;
|
|
5982
|
+
const isSimpleCallee = callee.type === "Identifier" ||
|
|
5983
|
+
(callee.type === "MemberExpression" &&
|
|
5984
|
+
callee.object.type === "Identifier" &&
|
|
5985
|
+
callee.property.type === "Identifier");
|
|
5986
|
+
|
|
5987
|
+
if (!isSimpleCallee) return false;
|
|
5988
|
+
|
|
5989
|
+
if (expr.arguments.length === 0) return true;
|
|
5990
|
+
if (expr.arguments.length === 1 && isSimpleArgHandler(expr.arguments[0])) return true;
|
|
5991
|
+
|
|
5992
|
+
return false;
|
|
5993
|
+
}
|
|
5994
|
+
|
|
5913
5995
|
return false;
|
|
5914
5996
|
};
|
|
5915
5997
|
|
|
@@ -6436,7 +6518,19 @@ const jsxSimpleElementOneLine = {
|
|
|
6436
6518
|
create(context) {
|
|
6437
6519
|
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
6438
6520
|
|
|
6439
|
-
// Check if an
|
|
6521
|
+
// Check if an argument is simple (identifier, literal, or simple member expression)
|
|
6522
|
+
const isSimpleArgHandler = (node) => {
|
|
6523
|
+
if (!node) return false;
|
|
6524
|
+
if (node.type === "Identifier") return true;
|
|
6525
|
+
if (node.type === "Literal") return true;
|
|
6526
|
+
if (node.type === "MemberExpression") {
|
|
6527
|
+
return node.object.type === "Identifier" && node.property.type === "Identifier";
|
|
6528
|
+
}
|
|
6529
|
+
|
|
6530
|
+
return false;
|
|
6531
|
+
};
|
|
6532
|
+
|
|
6533
|
+
// Check if an expression is simple (identifier, literal, member expression, or simple function call)
|
|
6440
6534
|
const isSimpleExpressionHandler = (node) => {
|
|
6441
6535
|
if (!node) return false;
|
|
6442
6536
|
|
|
@@ -6447,6 +6541,24 @@ const jsxSimpleElementOneLine = {
|
|
|
6447
6541
|
return node.object.type === "Identifier" && node.property.type === "Identifier";
|
|
6448
6542
|
}
|
|
6449
6543
|
|
|
6544
|
+
// Allow simple function calls with 0-1 simple arguments
|
|
6545
|
+
if (node.type === "CallExpression") {
|
|
6546
|
+
// Check callee is simple (identifier or member expression)
|
|
6547
|
+
const { callee } = node;
|
|
6548
|
+
const isSimpleCallee = callee.type === "Identifier" ||
|
|
6549
|
+
(callee.type === "MemberExpression" &&
|
|
6550
|
+
callee.object.type === "Identifier" &&
|
|
6551
|
+
callee.property.type === "Identifier");
|
|
6552
|
+
|
|
6553
|
+
if (!isSimpleCallee) return false;
|
|
6554
|
+
|
|
6555
|
+
// Allow 0-1 arguments, and the argument must be simple
|
|
6556
|
+
if (node.arguments.length === 0) return true;
|
|
6557
|
+
if (node.arguments.length === 1 && isSimpleArgHandler(node.arguments[0])) return true;
|
|
6558
|
+
|
|
6559
|
+
return false;
|
|
6560
|
+
}
|
|
6561
|
+
|
|
6450
6562
|
return false;
|
|
6451
6563
|
};
|
|
6452
6564
|
|
|
@@ -11565,13 +11677,16 @@ const simpleCallSingleLine = {
|
|
|
11565
11677
|
// Argument must be arrow function
|
|
11566
11678
|
if (arg.type !== "ArrowFunctionExpression") return;
|
|
11567
11679
|
|
|
11680
|
+
// Callbacks with 2+ params should be multiline (handled by function-params-per-line)
|
|
11681
|
+
if (arg.params.length >= 2) return;
|
|
11682
|
+
|
|
11568
11683
|
const { body } = arg;
|
|
11569
11684
|
|
|
11570
11685
|
// Zero params: body must be a simple call/import
|
|
11571
11686
|
if (arg.params.length === 0 && !isSimpleBodyHandler(body)) return;
|
|
11572
11687
|
|
|
11573
|
-
// With
|
|
11574
|
-
if (arg.params.length
|
|
11688
|
+
// With single param: body must be expression (not block) and simple
|
|
11689
|
+
if (arg.params.length === 1) {
|
|
11575
11690
|
if (body.type === "BlockStatement") return;
|
|
11576
11691
|
if (!isSimpleExpressionHandler(body)) return;
|
|
11577
11692
|
}
|
|
@@ -12299,15 +12414,32 @@ const functionObjectDestructure = {
|
|
|
12299
12414
|
create(context) {
|
|
12300
12415
|
const sourceCode = context.sourceCode || context.getSourceCode();
|
|
12301
12416
|
|
|
12302
|
-
// Track imports from
|
|
12303
|
-
|
|
12417
|
+
// Track imports from module paths that should use dot notation, not destructure
|
|
12418
|
+
// This improves searchability: api.loginHandler is easier to find than loginHandler
|
|
12419
|
+
const moduleImports = new Set();
|
|
12304
12420
|
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12421
|
+
// Folders that contain modules which should be accessed via dot notation
|
|
12422
|
+
const modulePathPatterns = [
|
|
12423
|
+
"api",
|
|
12424
|
+
"apis",
|
|
12425
|
+
"config",
|
|
12426
|
+
"configs",
|
|
12427
|
+
"constants",
|
|
12428
|
+
"data",
|
|
12429
|
+
"helpers",
|
|
12430
|
+
"lib",
|
|
12431
|
+
"routes",
|
|
12432
|
+
"services",
|
|
12433
|
+
"utils",
|
|
12434
|
+
"utilities",
|
|
12435
|
+
];
|
|
12436
|
+
|
|
12437
|
+
const isModuleImportPath = (importPath) => {
|
|
12438
|
+
// Match paths like @/services, @/constants, ./data, ../config, etc.
|
|
12439
|
+
return modulePathPatterns.some((pattern) => importPath === `@/${pattern}`
|
|
12440
|
+
|| importPath.endsWith(`/${pattern}`)
|
|
12441
|
+
|| importPath.includes(`/${pattern}/`)
|
|
12442
|
+
|| new RegExp(`^\\.?\\.?/${pattern}$`).test(importPath));
|
|
12311
12443
|
};
|
|
12312
12444
|
|
|
12313
12445
|
const checkImportHandler = (node) => {
|
|
@@ -12315,13 +12447,13 @@ const functionObjectDestructure = {
|
|
|
12315
12447
|
|
|
12316
12448
|
const importPath = node.source.value;
|
|
12317
12449
|
|
|
12318
|
-
if (
|
|
12319
|
-
// Track all imported specifiers from
|
|
12450
|
+
if (isModuleImportPath(importPath)) {
|
|
12451
|
+
// Track all imported specifiers from module paths
|
|
12320
12452
|
node.specifiers.forEach((spec) => {
|
|
12321
12453
|
if (spec.type === "ImportSpecifier" && spec.local && spec.local.name) {
|
|
12322
|
-
|
|
12454
|
+
moduleImports.add(spec.local.name);
|
|
12323
12455
|
} else if (spec.type === "ImportDefaultSpecifier" && spec.local && spec.local.name) {
|
|
12324
|
-
|
|
12456
|
+
moduleImports.add(spec.local.name);
|
|
12325
12457
|
}
|
|
12326
12458
|
});
|
|
12327
12459
|
}
|
|
@@ -12352,7 +12484,7 @@ const functionObjectDestructure = {
|
|
|
12352
12484
|
}
|
|
12353
12485
|
}
|
|
12354
12486
|
|
|
12355
|
-
if (sourceVarName &&
|
|
12487
|
+
if (sourceVarName && moduleImports.has(sourceVarName)) {
|
|
12356
12488
|
const destructuredProps = decl.id.properties
|
|
12357
12489
|
.filter((p) => p.type === "Property" && p.key && p.key.name)
|
|
12358
12490
|
.map((p) => p.key.name);
|
|
@@ -12360,7 +12492,7 @@ const functionObjectDestructure = {
|
|
|
12360
12492
|
const sourceText = sourceCode.getText(decl.init);
|
|
12361
12493
|
|
|
12362
12494
|
context.report({
|
|
12363
|
-
message: `Do not destructure
|
|
12495
|
+
message: `Do not destructure module imports. Use dot notation for searchability: "${sourceText}.${destructuredProps[0]}" instead of destructuring`,
|
|
12364
12496
|
node: decl.id,
|
|
12365
12497
|
});
|
|
12366
12498
|
}
|
|
@@ -13186,8 +13318,19 @@ const componentPropsDestructure = {
|
|
|
13186
13318
|
? (fixer) => {
|
|
13187
13319
|
const fixes = [];
|
|
13188
13320
|
|
|
13321
|
+
// Build destructured pattern, preserving type annotation if present
|
|
13322
|
+
const destructuredPattern = `{ ${accessedProps.join(", ")} }`;
|
|
13323
|
+
let replacement = destructuredPattern;
|
|
13324
|
+
|
|
13325
|
+
// Preserve TypeScript type annotation if present
|
|
13326
|
+
if (firstParam.typeAnnotation) {
|
|
13327
|
+
const typeText = sourceCode.getText(firstParam.typeAnnotation);
|
|
13328
|
+
|
|
13329
|
+
replacement = `${destructuredPattern}${typeText}`;
|
|
13330
|
+
}
|
|
13331
|
+
|
|
13189
13332
|
// Replace param with destructured pattern
|
|
13190
|
-
fixes.push(fixer.replaceText(firstParam,
|
|
13333
|
+
fixes.push(fixer.replaceText(firstParam, replacement));
|
|
13191
13334
|
|
|
13192
13335
|
// Replace all props.x with just x
|
|
13193
13336
|
accesses.forEach((access) => {
|
package/package.json
CHANGED