@blumintinc/eslint-plugin-blumint 1.4.0 → 1.5.1
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/lib/index.js +19 -1
- package/lib/rules/array-methods-this-context.js +2 -2
- package/lib/rules/enforce-callback-memo.js +3 -3
- package/lib/rules/enforce-dynamic-firebase-imports.js +2 -2
- package/lib/rules/enforce-exported-function-types.js +115 -85
- package/lib/rules/enforce-firestore-doc-ref-generic.js +2 -2
- package/lib/rules/enforce-firestore-path-utils.js +2 -2
- package/lib/rules/enforce-firestore-set-merge.js +35 -4
- package/lib/rules/enforce-identifiable-firestore-type.js +2 -2
- package/lib/rules/enforce-memoize-async.js +6 -4
- package/lib/rules/enforce-mock-firestore.js +3 -3
- package/lib/rules/enforce-realtimedb-path-utils.js +1 -1
- package/lib/rules/enforce-safe-stringify.js +2 -2
- package/lib/rules/enforce-serializable-params.js +3 -3
- package/lib/rules/enforce-verb-noun-naming.js +34 -12
- package/lib/rules/no-async-array-filter.js +2 -2
- package/lib/rules/no-async-foreach.js +1 -1
- package/lib/rules/no-class-instance-destructuring.d.ts +1 -0
- package/lib/rules/no-class-instance-destructuring.js +95 -0
- package/lib/rules/no-compositing-layer-props.js +1 -1
- package/lib/rules/no-conditional-literals-in-jsx.js +1 -1
- package/lib/rules/no-entire-object-hook-deps.js +1 -1
- package/lib/rules/no-explicit-return-type.d.ts +1 -0
- package/lib/rules/no-explicit-return-type.js +5 -2
- package/lib/rules/no-filter-without-return.js +1 -1
- package/lib/rules/no-firestore-object-arrays.d.ts +1 -0
- package/lib/rules/no-firestore-object-arrays.js +86 -0
- package/lib/rules/no-jsx-in-hooks.d.ts +1 -0
- package/lib/rules/no-jsx-in-hooks.js +200 -0
- package/lib/rules/no-memoize-on-static.d.ts +1 -0
- package/lib/rules/no-memoize-on-static.js +54 -0
- package/lib/rules/no-misused-switch-case.js +2 -2
- package/lib/rules/no-redundant-param-types.d.ts +2 -0
- package/lib/rules/no-redundant-param-types.js +129 -0
- package/lib/rules/no-unsafe-firestore-spread.d.ts +3 -0
- package/lib/rules/no-unsafe-firestore-spread.js +125 -0
- package/lib/rules/no-unused-props.js +1 -1
- package/lib/rules/no-useless-fragment.js +1 -1
- package/lib/rules/prefer-destructuring-no-class.d.ts +8 -0
- package/lib/rules/prefer-destructuring-no-class.js +200 -0
- package/lib/rules/require-hooks-default-params.d.ts +1 -0
- package/lib/rules/require-hooks-default-params.js +195 -0
- package/lib/rules/require-usememo-object-literals.js +1 -1
- package/lib/rules/semantic-function-prefixes.js +43 -4
- package/package.json +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noRedundantParamTypes = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
function isIdentifierWithTypeAnnotation(node) {
|
|
7
|
+
return (node.type === utils_1.AST_NODE_TYPES.Identifier && node.typeAnnotation !== undefined);
|
|
8
|
+
}
|
|
9
|
+
function isRestElementWithTypeAnnotation(node) {
|
|
10
|
+
return (node.type === utils_1.AST_NODE_TYPES.RestElement &&
|
|
11
|
+
node.typeAnnotation !== undefined);
|
|
12
|
+
}
|
|
13
|
+
function isObjectPatternWithTypeAnnotation(node) {
|
|
14
|
+
return (node.type === utils_1.AST_NODE_TYPES.ObjectPattern &&
|
|
15
|
+
node.typeAnnotation !== undefined);
|
|
16
|
+
}
|
|
17
|
+
function isArrayPatternWithTypeAnnotation(node) {
|
|
18
|
+
return (node.type === utils_1.AST_NODE_TYPES.ArrayPattern &&
|
|
19
|
+
node.typeAnnotation !== undefined);
|
|
20
|
+
}
|
|
21
|
+
function isAssignmentPatternWithTypeAnnotation(node) {
|
|
22
|
+
return (node.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
|
|
23
|
+
node.left.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
24
|
+
node.left.typeAnnotation !== undefined);
|
|
25
|
+
}
|
|
26
|
+
function removeTypeAnnotation(fixer, typeAnnotation, sourceCode) {
|
|
27
|
+
const typeStart = typeAnnotation.range[0];
|
|
28
|
+
const typeEnd = typeAnnotation.range[1];
|
|
29
|
+
// Check if there's a question mark before the type annotation
|
|
30
|
+
const hasQuestionMark = typeStart > 0 && sourceCode.getText().charAt(typeStart - 1) === '?';
|
|
31
|
+
const startPos = hasQuestionMark ? typeStart - 1 : typeStart;
|
|
32
|
+
return fixer.removeRange([startPos, typeEnd]);
|
|
33
|
+
}
|
|
34
|
+
function hasRedundantTypeAnnotation(node) {
|
|
35
|
+
const parent = node.parent;
|
|
36
|
+
if (!parent)
|
|
37
|
+
return false;
|
|
38
|
+
// Check variable declarations
|
|
39
|
+
if (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
40
|
+
parent.id.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
// Check class property assignments
|
|
44
|
+
if (parent.type === utils_1.AST_NODE_TYPES.PropertyDefinition &&
|
|
45
|
+
parent.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
// Check assignments
|
|
49
|
+
if (parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
50
|
+
parent.left.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
51
|
+
parent.left.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
exports.noRedundantParamTypes = (0, createRule_1.createRule)({
|
|
57
|
+
name: 'no-redundant-param-types',
|
|
58
|
+
meta: {
|
|
59
|
+
type: 'suggestion',
|
|
60
|
+
docs: {
|
|
61
|
+
description: 'Disallow redundant parameter type annotations',
|
|
62
|
+
recommended: 'error',
|
|
63
|
+
},
|
|
64
|
+
fixable: 'code',
|
|
65
|
+
schema: [],
|
|
66
|
+
messages: {
|
|
67
|
+
redundantParamType: 'Parameter type annotation is redundant',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
defaultOptions: [],
|
|
71
|
+
create(context) {
|
|
72
|
+
return {
|
|
73
|
+
ArrowFunctionExpression(node) {
|
|
74
|
+
if (!hasRedundantTypeAnnotation(node))
|
|
75
|
+
return;
|
|
76
|
+
const params = node.params;
|
|
77
|
+
params.forEach((param) => {
|
|
78
|
+
if (isIdentifierWithTypeAnnotation(param)) {
|
|
79
|
+
context.report({
|
|
80
|
+
node: param,
|
|
81
|
+
messageId: 'redundantParamType',
|
|
82
|
+
fix(fixer) {
|
|
83
|
+
return removeTypeAnnotation(fixer, param.typeAnnotation, context.getSourceCode());
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else if (isRestElementWithTypeAnnotation(param)) {
|
|
88
|
+
context.report({
|
|
89
|
+
node: param,
|
|
90
|
+
messageId: 'redundantParamType',
|
|
91
|
+
fix(fixer) {
|
|
92
|
+
return removeTypeAnnotation(fixer, param.typeAnnotation, context.getSourceCode());
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
else if (isObjectPatternWithTypeAnnotation(param)) {
|
|
97
|
+
context.report({
|
|
98
|
+
node: param,
|
|
99
|
+
messageId: 'redundantParamType',
|
|
100
|
+
fix(fixer) {
|
|
101
|
+
return removeTypeAnnotation(fixer, param.typeAnnotation, context.getSourceCode());
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else if (isArrayPatternWithTypeAnnotation(param)) {
|
|
106
|
+
context.report({
|
|
107
|
+
node: param,
|
|
108
|
+
messageId: 'redundantParamType',
|
|
109
|
+
fix(fixer) {
|
|
110
|
+
return removeTypeAnnotation(fixer, param.typeAnnotation, context.getSourceCode());
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (isAssignmentPatternWithTypeAnnotation(param)) {
|
|
115
|
+
const { left } = param;
|
|
116
|
+
context.report({
|
|
117
|
+
node: param,
|
|
118
|
+
messageId: 'redundantParamType',
|
|
119
|
+
fix(fixer) {
|
|
120
|
+
return removeTypeAnnotation(fixer, left.typeAnnotation, context.getSourceCode());
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=no-redundant-param-types.js.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noUnsafeFirestoreSpread = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.noUnsafeFirestoreSpread = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'no-unsafe-firestore-spread',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Prevent unsafe object/array spreads in Firestore updates',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
unsafeObjectSpread: 'Avoid using object spread in Firestore updates. Use dot notation with FieldPath instead.',
|
|
18
|
+
unsafeArraySpread: 'Avoid using array spread in Firestore updates. Use FieldValue.arrayUnion() instead.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
function isFirestoreSetMergeCall(node) {
|
|
24
|
+
// Check for merge: true in the last argument
|
|
25
|
+
const lastArg = node.arguments[node.arguments.length - 1];
|
|
26
|
+
if (lastArg?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
27
|
+
const hasMergeTrue = lastArg.properties.some((prop) => prop.type === utils_1.AST_NODE_TYPES.Property &&
|
|
28
|
+
prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
29
|
+
prop.key.name === 'merge' &&
|
|
30
|
+
prop.value.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
31
|
+
prop.value.value === true);
|
|
32
|
+
if (!hasMergeTrue)
|
|
33
|
+
return false;
|
|
34
|
+
// Check if it's a set() method call or setDoc() function call
|
|
35
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
36
|
+
const property = node.callee.property;
|
|
37
|
+
return (property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
38
|
+
property.name === 'set');
|
|
39
|
+
}
|
|
40
|
+
else if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
41
|
+
return node.callee.name === 'setDoc';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function checkObjectForSpreads(node, parentPath = '') {
|
|
47
|
+
for (const property of node.properties) {
|
|
48
|
+
if (property.type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
49
|
+
context.report({
|
|
50
|
+
node: property,
|
|
51
|
+
messageId: 'unsafeObjectSpread',
|
|
52
|
+
fix: null,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else if (property.type === utils_1.AST_NODE_TYPES.Property) {
|
|
56
|
+
const key = property.key.type === utils_1.AST_NODE_TYPES.Identifier
|
|
57
|
+
? property.key.name
|
|
58
|
+
: '';
|
|
59
|
+
const newPath = parentPath ? `${parentPath}.${key}` : key;
|
|
60
|
+
if (property.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
61
|
+
checkObjectForSpreads(property.value, newPath);
|
|
62
|
+
}
|
|
63
|
+
else if (property.value.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
|
|
64
|
+
checkArrayForSpreads(property.value);
|
|
65
|
+
}
|
|
66
|
+
else if (property.value.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
67
|
+
// Handle chained array methods like [...array].filter()
|
|
68
|
+
let current = property.value;
|
|
69
|
+
while (current) {
|
|
70
|
+
if (current.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
|
|
71
|
+
checkArrayForSpreads(current);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
else if (current.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
75
|
+
checkObjectForSpreads(current, newPath);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
// Move up to check the caller if it's a method chain
|
|
79
|
+
if (current.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
80
|
+
current.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
81
|
+
current = current.callee.object;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function checkArrayForSpreads(node) {
|
|
92
|
+
// Check for spreads in the array expression itself
|
|
93
|
+
for (const element of node.elements) {
|
|
94
|
+
if (element?.type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
95
|
+
context.report({
|
|
96
|
+
node: element,
|
|
97
|
+
messageId: 'unsafeArraySpread',
|
|
98
|
+
fix: null,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
CallExpression(node) {
|
|
105
|
+
if (!isFirestoreSetMergeCall(node))
|
|
106
|
+
return;
|
|
107
|
+
// For set() calls, the update object can be either the first or second argument
|
|
108
|
+
// If it's a docRef.set(data) call, it's the first argument
|
|
109
|
+
// If it's a docRef.set(docRef, data) call (like in transactions/batches), it's the second argument
|
|
110
|
+
let updateArg;
|
|
111
|
+
if (node.arguments.length === 2) {
|
|
112
|
+
updateArg = node.arguments[0];
|
|
113
|
+
}
|
|
114
|
+
else if (node.arguments.length === 3) {
|
|
115
|
+
// In a transaction or batch operation, the data is the second argument
|
|
116
|
+
updateArg = node.arguments[1];
|
|
117
|
+
}
|
|
118
|
+
if (!updateArg || updateArg.type !== utils_1.AST_NODE_TYPES.ObjectExpression)
|
|
119
|
+
return;
|
|
120
|
+
checkObjectForSpreads(updateArg);
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=no-unsafe-firestore-spread.js.map
|
|
@@ -13,7 +13,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
13
13
|
},
|
|
14
14
|
schema: [],
|
|
15
15
|
messages: {
|
|
16
|
-
unusedProp: 'Prop "{{propName}}" is defined in type but not used in component',
|
|
16
|
+
unusedProp: 'Prop "{{propName}}" is defined in the Props type but not used in the component. Either use the prop in your component or remove it from the Props type. If you need to forward all props, use a rest spread operator: `const MyComponent = ({ usedProp, ...rest }: Props) => ...`',
|
|
17
17
|
},
|
|
18
18
|
fixable: 'code',
|
|
19
19
|
},
|
|
@@ -38,7 +38,7 @@ exports.noUselessFragment = {
|
|
|
38
38
|
recommended: 'error',
|
|
39
39
|
},
|
|
40
40
|
messages: {
|
|
41
|
-
noUselessFragment: 'React fragment is unnecessary when wrapping a single child',
|
|
41
|
+
noUselessFragment: 'React fragment is unnecessary when wrapping a single child. Instead of `<>{"text"}</> or <Fragment>{"text"}</Fragment>`, use just `{"text"}`. Fragments are only needed when returning multiple elements.',
|
|
42
42
|
},
|
|
43
43
|
schema: [],
|
|
44
44
|
fixable: 'code',
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
object?: boolean;
|
|
4
|
+
enforceForRenamedProperties?: boolean;
|
|
5
|
+
}
|
|
6
|
+
];
|
|
7
|
+
export declare const preferDestructuringNoClass: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"preferDestructuring", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.preferDestructuringNoClass = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const defaultOptions = [
|
|
7
|
+
{
|
|
8
|
+
object: true,
|
|
9
|
+
enforceForRenamedProperties: false,
|
|
10
|
+
},
|
|
11
|
+
];
|
|
12
|
+
function isClassInstance(node, context) {
|
|
13
|
+
// Check if node is a MemberExpression
|
|
14
|
+
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
15
|
+
const object = node.object;
|
|
16
|
+
// If object is a NewExpression, it's a class instance
|
|
17
|
+
if (object.type === utils_1.AST_NODE_TYPES.NewExpression) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
// If object is an identifier, check if it refers to a class instance
|
|
21
|
+
if (object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
22
|
+
const variable = object.name;
|
|
23
|
+
const scope = context.getScope();
|
|
24
|
+
const ref = scope.references.find((ref) => ref.identifier.name === variable);
|
|
25
|
+
if (ref?.resolved?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
26
|
+
const init = ref.resolved.defs[0].node.init;
|
|
27
|
+
return init?.type === utils_1.AST_NODE_TYPES.NewExpression;
|
|
28
|
+
}
|
|
29
|
+
// Check if the identifier refers to a class (not an instance)
|
|
30
|
+
if (ref?.resolved?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.ClassDeclaration) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Recursively check if parent object is a class instance
|
|
35
|
+
if (object.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
36
|
+
return isClassInstance(object, context);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function isStaticClassMember(node, context) {
|
|
42
|
+
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
43
|
+
const object = node.object;
|
|
44
|
+
if (object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
45
|
+
const variable = object.name;
|
|
46
|
+
const scope = context.getScope();
|
|
47
|
+
const ref = scope.references.find((ref) => ref.identifier.name === variable);
|
|
48
|
+
return (ref?.resolved?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.ClassDeclaration);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if the property name matches the variable name in an assignment
|
|
55
|
+
*/
|
|
56
|
+
function isMatchingPropertyName(propertyNode, variableName) {
|
|
57
|
+
if (propertyNode.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
58
|
+
return propertyNode.name === variableName;
|
|
59
|
+
}
|
|
60
|
+
if (propertyNode.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
61
|
+
return propertyNode.value === variableName;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get the property text for destructuring
|
|
67
|
+
*/
|
|
68
|
+
function getPropertyText(property, computed, sourceCode) {
|
|
69
|
+
if (computed) {
|
|
70
|
+
return sourceCode.getText(property);
|
|
71
|
+
}
|
|
72
|
+
if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
73
|
+
return property.name;
|
|
74
|
+
}
|
|
75
|
+
if (property.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
76
|
+
return String(property.value);
|
|
77
|
+
}
|
|
78
|
+
// For any other type, use the source text
|
|
79
|
+
return sourceCode.getText(property);
|
|
80
|
+
}
|
|
81
|
+
exports.preferDestructuringNoClass = (0, createRule_1.createRule)({
|
|
82
|
+
name: 'prefer-destructuring-no-class',
|
|
83
|
+
meta: {
|
|
84
|
+
type: 'suggestion',
|
|
85
|
+
docs: {
|
|
86
|
+
description: 'Enforce destructuring when accessing object properties, except for class instances',
|
|
87
|
+
recommended: 'error',
|
|
88
|
+
},
|
|
89
|
+
fixable: 'code',
|
|
90
|
+
schema: [
|
|
91
|
+
{
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
object: {
|
|
95
|
+
type: 'boolean',
|
|
96
|
+
default: true,
|
|
97
|
+
},
|
|
98
|
+
enforceForRenamedProperties: {
|
|
99
|
+
type: 'boolean',
|
|
100
|
+
default: false,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
additionalProperties: false,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
messages: {
|
|
107
|
+
preferDestructuring: 'Use destructuring instead of accessing the property directly.',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
defaultOptions,
|
|
111
|
+
create(context) {
|
|
112
|
+
const options = {
|
|
113
|
+
object: defaultOptions[0].object,
|
|
114
|
+
enforceForRenamedProperties: defaultOptions[0].enforceForRenamedProperties,
|
|
115
|
+
...context.options[0],
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Check if destructuring should be used for this node
|
|
119
|
+
*/
|
|
120
|
+
function shouldUseDestructuring(node, leftNode) {
|
|
121
|
+
// Skip if this is a class instance or static class member
|
|
122
|
+
if (isClassInstance(node, context) ||
|
|
123
|
+
isStaticClassMember(node, context)) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// Check object destructuring
|
|
127
|
+
if (options.object) {
|
|
128
|
+
if (options.enforceForRenamedProperties) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
// Only suggest destructuring when property name matches variable name
|
|
132
|
+
if (leftNode.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
133
|
+
return isMatchingPropertyName(node.property, leftNode.name);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
VariableDeclarator(node) {
|
|
140
|
+
// Skip if variable is declared without assignment or if init is not a MemberExpression
|
|
141
|
+
if (!node.init)
|
|
142
|
+
return;
|
|
143
|
+
if (node.init.type !== utils_1.AST_NODE_TYPES.MemberExpression)
|
|
144
|
+
return;
|
|
145
|
+
if (shouldUseDestructuring(node.init, node.id)) {
|
|
146
|
+
const sourceCode = context.getSourceCode();
|
|
147
|
+
const objectText = sourceCode.getText(node.init.object);
|
|
148
|
+
const propertyText = getPropertyText(node.init.property, node.init.computed, sourceCode);
|
|
149
|
+
context.report({
|
|
150
|
+
node,
|
|
151
|
+
messageId: 'preferDestructuring',
|
|
152
|
+
fix(fixer) {
|
|
153
|
+
// Get the variable declaration kind (const, let, var)
|
|
154
|
+
const parentNode = node.parent;
|
|
155
|
+
if (!parentNode ||
|
|
156
|
+
parentNode.type !== utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const kind = parentNode.kind;
|
|
160
|
+
// Handle renamed properties
|
|
161
|
+
if (options.enforceForRenamedProperties &&
|
|
162
|
+
node.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
163
|
+
node.init &&
|
|
164
|
+
node.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
165
|
+
!isMatchingPropertyName(node.init.property, node.id.name)) {
|
|
166
|
+
return fixer.replaceText(parentNode, `${kind} { ${propertyText}: ${node.id.name} } = ${objectText};`);
|
|
167
|
+
}
|
|
168
|
+
return fixer.replaceText(parentNode, `${kind} { ${propertyText} } = ${objectText};`);
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
AssignmentExpression(node) {
|
|
174
|
+
if (node.operator === '=' &&
|
|
175
|
+
node.right.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
176
|
+
if (shouldUseDestructuring(node.right, node.left)) {
|
|
177
|
+
const sourceCode = context.getSourceCode();
|
|
178
|
+
const objectText = sourceCode.getText(node.right.object);
|
|
179
|
+
const propertyText = getPropertyText(node.right.property, node.right.computed, sourceCode);
|
|
180
|
+
context.report({
|
|
181
|
+
node,
|
|
182
|
+
messageId: 'preferDestructuring',
|
|
183
|
+
fix(fixer) {
|
|
184
|
+
// Handle renamed properties
|
|
185
|
+
if (options.enforceForRenamedProperties &&
|
|
186
|
+
node.left.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
187
|
+
node.right.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
188
|
+
!isMatchingPropertyName(node.right.property, node.left.name)) {
|
|
189
|
+
return fixer.replaceText(node, `({ ${propertyText}: ${node.left.name} } = ${objectText})`);
|
|
190
|
+
}
|
|
191
|
+
return fixer.replaceText(node, `({ ${propertyText} } = ${objectText})`);
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
//# sourceMappingURL=prefer-destructuring-no-class.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const requireHooksDefaultParams: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"requireDefaultParams", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|