@blumintinc/eslint-plugin-blumint 1.5.4 → 1.6.0
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/enforce-assert-throws.d.ts +1 -0
- package/lib/rules/enforce-assert-throws.js +109 -0
- package/lib/rules/enforce-exported-function-types.js +3 -2
- package/lib/rules/enforce-firestore-facade.d.ts +3 -0
- package/lib/rules/enforce-firestore-facade.js +126 -0
- package/lib/rules/enforce-firestore-set-merge.js +83 -2
- package/lib/rules/enforce-verb-noun-naming.js +1 -0
- package/lib/rules/no-complex-cloud-params.d.ts +1 -0
- package/lib/rules/no-complex-cloud-params.js +363 -0
- package/lib/rules/no-explicit-return-type.js +27 -5
- package/lib/rules/no-mixed-firestore-transactions.d.ts +1 -0
- package/lib/rules/no-mixed-firestore-transactions.js +115 -0
- package/lib/rules/no-unused-props.js +28 -2
- package/lib/rules/prefer-batch-operations.d.ts +3 -0
- package/lib/rules/prefer-batch-operations.js +176 -0
- package/lib/rules/prefer-settings-object.js +136 -6
- package/lib/rules/semantic-function-prefixes.js +7 -0
- package/lib/rules/sync-onwrite-name-func.d.ts +1 -0
- package/lib/rules/sync-onwrite-name-func.js +79 -0
- package/package.json +1 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.preferBatchOperations = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const SETTER_METHODS = new Set(['set', 'overwrite']);
|
|
7
|
+
const ARRAY_METHODS = new Set(['map', 'forEach', 'filter', 'reduce', 'every', 'some']);
|
|
8
|
+
function isArrayMethod(node) {
|
|
9
|
+
if (node.type !== utils_1.AST_NODE_TYPES.CallExpression)
|
|
10
|
+
return { isValid: false };
|
|
11
|
+
const callee = node.callee;
|
|
12
|
+
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
13
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
14
|
+
ARRAY_METHODS.has(callee.property.name)) {
|
|
15
|
+
return { isValid: true, methodName: callee.property.name };
|
|
16
|
+
}
|
|
17
|
+
return { isValid: false };
|
|
18
|
+
}
|
|
19
|
+
function isPromiseAll(node) {
|
|
20
|
+
if (node.type !== utils_1.AST_NODE_TYPES.CallExpression)
|
|
21
|
+
return false;
|
|
22
|
+
const callee = node.callee;
|
|
23
|
+
return (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
24
|
+
callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
25
|
+
callee.object.name === 'Promise' &&
|
|
26
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
27
|
+
callee.property.name === 'all');
|
|
28
|
+
}
|
|
29
|
+
function findLoopNode(node) {
|
|
30
|
+
let current = node;
|
|
31
|
+
let loopNode = null;
|
|
32
|
+
while (current) {
|
|
33
|
+
switch (current.type) {
|
|
34
|
+
case utils_1.AST_NODE_TYPES.ForStatement:
|
|
35
|
+
case utils_1.AST_NODE_TYPES.ForInStatement:
|
|
36
|
+
case utils_1.AST_NODE_TYPES.ForOfStatement:
|
|
37
|
+
case utils_1.AST_NODE_TYPES.WhileStatement:
|
|
38
|
+
case utils_1.AST_NODE_TYPES.DoWhileStatement:
|
|
39
|
+
loopNode = current;
|
|
40
|
+
break;
|
|
41
|
+
case utils_1.AST_NODE_TYPES.CallExpression:
|
|
42
|
+
// Check for Promise.all
|
|
43
|
+
if (isPromiseAll(current)) {
|
|
44
|
+
return { node: current, isArrayMethod: 'map' };
|
|
45
|
+
}
|
|
46
|
+
// Check for array methods
|
|
47
|
+
const { isValid, methodName: currentMethodName } = isArrayMethod(current);
|
|
48
|
+
if (isValid && currentMethodName) {
|
|
49
|
+
// For sequential array methods, check if the callback is async
|
|
50
|
+
if (currentMethodName === 'forEach' || currentMethodName === 'reduce' || currentMethodName === 'filter') {
|
|
51
|
+
const callback = current.arguments[0];
|
|
52
|
+
if (callback && (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
53
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
54
|
+
callback.async) {
|
|
55
|
+
return { node: current, isArrayMethod: currentMethodName };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { node: current, isArrayMethod: currentMethodName };
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case utils_1.AST_NODE_TYPES.Program:
|
|
62
|
+
// Return loop if we found one
|
|
63
|
+
if (loopNode) {
|
|
64
|
+
return { node: loopNode };
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
current = current.parent;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
function isSetterMethodCall(node) {
|
|
73
|
+
if (node.type !== utils_1.AST_NODE_TYPES.CallExpression)
|
|
74
|
+
return { isValid: false };
|
|
75
|
+
const callee = node.callee;
|
|
76
|
+
if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression)
|
|
77
|
+
return { isValid: false };
|
|
78
|
+
if (callee.property.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
79
|
+
return { isValid: false };
|
|
80
|
+
if (!SETTER_METHODS.has(callee.property.name))
|
|
81
|
+
return { isValid: false };
|
|
82
|
+
// Get the setter instance
|
|
83
|
+
const object = callee.object;
|
|
84
|
+
if (object.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
85
|
+
return { isValid: false };
|
|
86
|
+
const setterInstance = object.name;
|
|
87
|
+
// Get the method name
|
|
88
|
+
const methodName = callee.property.name;
|
|
89
|
+
return { isValid: true, methodName, setterInstance };
|
|
90
|
+
}
|
|
91
|
+
const loopSetterCalls = new Map();
|
|
92
|
+
exports.preferBatchOperations = (0, createRule_1.createRule)({
|
|
93
|
+
name: 'prefer-batch-operations',
|
|
94
|
+
meta: {
|
|
95
|
+
type: 'suggestion',
|
|
96
|
+
docs: {
|
|
97
|
+
description: 'Enforce using setAll() and overwriteAll() instead of multiple set() or overwrite() calls',
|
|
98
|
+
recommended: 'error',
|
|
99
|
+
},
|
|
100
|
+
fixable: 'code',
|
|
101
|
+
schema: [],
|
|
102
|
+
messages: {
|
|
103
|
+
preferSetAll: 'Use setAll() instead of multiple set() calls for better performance',
|
|
104
|
+
preferOverwriteAll: 'Use overwriteAll() instead of multiple overwrite() calls for better performance',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
defaultOptions: [],
|
|
108
|
+
create(context) {
|
|
109
|
+
return {
|
|
110
|
+
'Program:exit'() {
|
|
111
|
+
// Clear the maps for the next file
|
|
112
|
+
loopSetterCalls.clear();
|
|
113
|
+
},
|
|
114
|
+
CallExpression(node) {
|
|
115
|
+
const { isValid, methodName, setterInstance } = isSetterMethodCall(node);
|
|
116
|
+
if (!isValid || !methodName || !setterInstance)
|
|
117
|
+
return;
|
|
118
|
+
// Check if we're in a loop or Promise.all
|
|
119
|
+
const loopInfo = findLoopNode(node);
|
|
120
|
+
if (!loopInfo)
|
|
121
|
+
return;
|
|
122
|
+
// Get or create the setter calls map for this loop
|
|
123
|
+
let setterCalls = loopSetterCalls.get(loopInfo.node);
|
|
124
|
+
if (!setterCalls) {
|
|
125
|
+
setterCalls = new Map();
|
|
126
|
+
loopSetterCalls.set(loopInfo.node, setterCalls);
|
|
127
|
+
}
|
|
128
|
+
// Track setter instance and method calls for this loop
|
|
129
|
+
const key = setterInstance;
|
|
130
|
+
const existing = setterCalls.get(key);
|
|
131
|
+
if (existing) {
|
|
132
|
+
// If we see a different method on the same setter instance, don't report
|
|
133
|
+
if (existing.methodName !== methodName)
|
|
134
|
+
return;
|
|
135
|
+
existing.count++;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
setterCalls.set(key, { methodName, count: 1 });
|
|
139
|
+
}
|
|
140
|
+
// Report on the first occurrence of a repeated call
|
|
141
|
+
// For Promise.all and array methods, report on the first occurrence
|
|
142
|
+
// For regular loops, report on the first occurrence too since we know it's in a loop
|
|
143
|
+
const shouldReport = loopInfo.isArrayMethod
|
|
144
|
+
? ['forEach', 'reduce', 'filter', 'map'].includes(loopInfo.isArrayMethod)
|
|
145
|
+
: setterCalls.get(key).count === 1;
|
|
146
|
+
// Don't report if we have multiple different setter instances in a loop
|
|
147
|
+
// Only check this for regular loops, not array methods or Promise.all
|
|
148
|
+
if (shouldReport && !loopInfo.isArrayMethod) {
|
|
149
|
+
const setterInstances = new Set(Array.from(setterCalls.keys()));
|
|
150
|
+
if (setterInstances.size > 1) {
|
|
151
|
+
// This is a valid use case when using multiple setters in a loop
|
|
152
|
+
// For example: userSetter.set(doc.user) and orderSetter.set(doc.order)
|
|
153
|
+
// Each setter operates on a different collection, so they can't be batched together
|
|
154
|
+
// We only want to report when using the same setter instance multiple times
|
|
155
|
+
// For example: userSetter.set(doc.user) multiple times should use userSetter.setAll()
|
|
156
|
+
if (loopInfo.node.type.startsWith('For') || loopInfo.node.type.startsWith('While') || loopInfo.node.type.startsWith('Do')) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Report on the first occurrence of a repeated call
|
|
162
|
+
// For Promise.all and array methods, report on the first occurrence
|
|
163
|
+
// For regular loops, report on the first occurrence too since we know it's in a loop
|
|
164
|
+
if (shouldReport) {
|
|
165
|
+
const messageId = methodName === 'set' ? 'preferSetAll' : 'preferOverwriteAll';
|
|
166
|
+
context.report({
|
|
167
|
+
node,
|
|
168
|
+
messageId,
|
|
169
|
+
fix: () => null, // We can't provide a fix because we don't know the array structure
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
//# sourceMappingURL=prefer-batch-operations.js.map
|
|
@@ -51,6 +51,16 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
|
|
|
51
51
|
if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
|
52
52
|
return getParameterType(param.left);
|
|
53
53
|
}
|
|
54
|
+
if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern && param.typeAnnotation) {
|
|
55
|
+
// For destructured parameters, use the type annotation name
|
|
56
|
+
const typeNode = param.typeAnnotation.typeAnnotation;
|
|
57
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
58
|
+
return typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier
|
|
59
|
+
? typeNode.typeName.name
|
|
60
|
+
: 'unknown';
|
|
61
|
+
}
|
|
62
|
+
return typeNode.type;
|
|
63
|
+
}
|
|
54
64
|
if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
|
|
55
65
|
const typeNode = param.typeAnnotation.typeAnnotation;
|
|
56
66
|
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
@@ -72,18 +82,140 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
|
|
|
72
82
|
const typeMap = new Map();
|
|
73
83
|
for (const param of params) {
|
|
74
84
|
const type = getParameterType(param);
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
const count = (typeMap.get(type) || 0) + 1;
|
|
86
|
+
typeMap.set(type, count);
|
|
87
|
+
if (count > 1) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
function isBuiltInOrThirdParty(node) {
|
|
94
|
+
// Check if the node is part of a new expression (constructor call)
|
|
95
|
+
let current = node;
|
|
96
|
+
while (current.parent) {
|
|
97
|
+
const parent = current.parent;
|
|
98
|
+
// Check if we're in a constructor call
|
|
99
|
+
if (parent.type === utils_1.AST_NODE_TYPES.NewExpression) {
|
|
100
|
+
const callee = parent.callee;
|
|
101
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
102
|
+
// List of built-in objects that should be ignored
|
|
103
|
+
const builtInObjects = new Set([
|
|
104
|
+
'Promise',
|
|
105
|
+
'Map',
|
|
106
|
+
'Set',
|
|
107
|
+
'WeakMap',
|
|
108
|
+
'WeakSet',
|
|
109
|
+
'Int8Array',
|
|
110
|
+
'Uint8Array',
|
|
111
|
+
'Uint8ClampedArray',
|
|
112
|
+
'Int16Array',
|
|
113
|
+
'Uint16Array',
|
|
114
|
+
'Int32Array',
|
|
115
|
+
'Uint32Array',
|
|
116
|
+
'Float32Array',
|
|
117
|
+
'Float64Array',
|
|
118
|
+
'BigInt64Array',
|
|
119
|
+
'BigUint64Array',
|
|
120
|
+
'ArrayBuffer',
|
|
121
|
+
'SharedArrayBuffer',
|
|
122
|
+
'DataView',
|
|
123
|
+
'Date',
|
|
124
|
+
'RegExp',
|
|
125
|
+
'Error',
|
|
126
|
+
'AggregateError',
|
|
127
|
+
'EvalError',
|
|
128
|
+
'RangeError',
|
|
129
|
+
'ReferenceError',
|
|
130
|
+
'SyntaxError',
|
|
131
|
+
'TypeError',
|
|
132
|
+
'URIError',
|
|
133
|
+
'Transform', // Added Transform to built-in objects
|
|
134
|
+
]);
|
|
135
|
+
if (builtInObjects.has(callee.name)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
// Check if the identifier is imported from a third-party module
|
|
139
|
+
const scope = context.getScope();
|
|
140
|
+
const variable = scope.variables.find((v) => v.name === callee.name);
|
|
141
|
+
if (variable) {
|
|
142
|
+
const def = variable.defs[0];
|
|
143
|
+
if (def?.type === 'ImportBinding') {
|
|
144
|
+
const importDecl = def.parent;
|
|
145
|
+
let source;
|
|
146
|
+
if (importDecl.type === utils_1.AST_NODE_TYPES.ImportDeclaration) {
|
|
147
|
+
source = importDecl.source.value;
|
|
148
|
+
}
|
|
149
|
+
else if (importDecl.type ===
|
|
150
|
+
utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration &&
|
|
151
|
+
importDecl.moduleReference.type ===
|
|
152
|
+
utils_1.AST_NODE_TYPES.TSExternalModuleReference &&
|
|
153
|
+
importDecl.moduleReference.expression.type ===
|
|
154
|
+
utils_1.AST_NODE_TYPES.Literal) {
|
|
155
|
+
source = importDecl.moduleReference.expression
|
|
156
|
+
.value;
|
|
157
|
+
}
|
|
158
|
+
// If it's a third-party module (doesn't start with '.' or '/'), ignore it
|
|
159
|
+
if (source &&
|
|
160
|
+
!source.startsWith('.') &&
|
|
161
|
+
!source.startsWith('/')) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
168
|
+
// Handle cases like React.Component or lodash.debounce
|
|
169
|
+
const obj = callee.object;
|
|
170
|
+
if (obj.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
171
|
+
const scope = context.getScope();
|
|
172
|
+
const variable = scope.variables.find((v) => v.name === obj.name);
|
|
173
|
+
if (variable) {
|
|
174
|
+
const def = variable.defs[0];
|
|
175
|
+
if (def?.type === 'ImportBinding') {
|
|
176
|
+
const importDecl = def.parent;
|
|
177
|
+
let source;
|
|
178
|
+
if (importDecl.type === utils_1.AST_NODE_TYPES.ImportDeclaration) {
|
|
179
|
+
source = importDecl.source.value;
|
|
180
|
+
}
|
|
181
|
+
else if (importDecl.type ===
|
|
182
|
+
utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration &&
|
|
183
|
+
importDecl.moduleReference.type ===
|
|
184
|
+
utils_1.AST_NODE_TYPES.TSExternalModuleReference &&
|
|
185
|
+
importDecl.moduleReference.expression.type ===
|
|
186
|
+
utils_1.AST_NODE_TYPES.Literal) {
|
|
187
|
+
source = importDecl.moduleReference.expression
|
|
188
|
+
.value;
|
|
189
|
+
}
|
|
190
|
+
// If it's a third-party module (doesn't start with '.' or '/'), ignore it
|
|
191
|
+
if (source &&
|
|
192
|
+
!source.startsWith('.') &&
|
|
193
|
+
!source.startsWith('/')) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Also check if we're in a property of an object that's passed to a constructor
|
|
202
|
+
if (parent.type === utils_1.AST_NODE_TYPES.Property &&
|
|
203
|
+
parent.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
|
|
204
|
+
parent.parent.parent?.type === utils_1.AST_NODE_TYPES.NewExpression) {
|
|
77
205
|
return true;
|
|
78
206
|
}
|
|
207
|
+
current = parent;
|
|
79
208
|
}
|
|
80
209
|
return false;
|
|
81
210
|
}
|
|
82
211
|
function shouldIgnoreNode(node) {
|
|
212
|
+
// Ignore built-in objects and third-party modules
|
|
213
|
+
if (isBuiltInOrThirdParty(node))
|
|
214
|
+
return true;
|
|
83
215
|
// Ignore variadic functions if configured
|
|
84
216
|
if (finalOptions.ignoreVariadicFunctions) {
|
|
85
217
|
const hasRestParam = node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
|
|
86
|
-
node.params.some(param => param.type === utils_1.AST_NODE_TYPES.RestElement);
|
|
218
|
+
node.params.some((param) => param.type === utils_1.AST_NODE_TYPES.RestElement);
|
|
87
219
|
if (hasRestParam)
|
|
88
220
|
return true;
|
|
89
221
|
}
|
|
@@ -105,9 +237,7 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
|
|
|
105
237
|
return;
|
|
106
238
|
const params = node.params;
|
|
107
239
|
// Check for too many parameters first
|
|
108
|
-
const minParams = finalOptions.minimumParameters
|
|
109
|
-
? finalOptions.minimumParameters
|
|
110
|
-
: defaultOptions.minimumParameters;
|
|
240
|
+
const minParams = finalOptions.minimumParameters ?? defaultOptions.minimumParameters ?? 3;
|
|
111
241
|
if (params.length >= minParams) {
|
|
112
242
|
context.report({
|
|
113
243
|
node,
|
|
@@ -4,6 +4,7 @@ exports.semanticFunctionPrefixes = void 0;
|
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const createRule_1 = require("../utils/createRule");
|
|
6
6
|
const DISALLOWED_PREFIXES = new Set(['get', 'update', 'check', 'manage', 'process', 'do']);
|
|
7
|
+
const NEXTJS_DATA_FUNCTIONS = new Set(['getServerSideProps', 'getStaticProps', 'getStaticPaths']);
|
|
7
8
|
const SUGGESTED_ALTERNATIVES = {
|
|
8
9
|
get: ['fetch', 'retrieve', 'compute', 'derive'],
|
|
9
10
|
update: ['modify', 'set', 'apply'],
|
|
@@ -38,6 +39,9 @@ exports.semanticFunctionPrefixes = (0, createRule_1.createRule)({
|
|
|
38
39
|
// Skip if method starts with 'is' (boolean check methods are okay)
|
|
39
40
|
if (methodName.startsWith('is'))
|
|
40
41
|
return;
|
|
42
|
+
// Skip Next.js data-fetching functions
|
|
43
|
+
if (NEXTJS_DATA_FUNCTIONS.has(methodName))
|
|
44
|
+
return;
|
|
41
45
|
// Extract first word from PascalCase/camelCase
|
|
42
46
|
let firstWord = methodName;
|
|
43
47
|
for (let i = 1; i < methodName.length; i++) {
|
|
@@ -79,6 +83,9 @@ exports.semanticFunctionPrefixes = (0, createRule_1.createRule)({
|
|
|
79
83
|
// Skip if function starts with 'is' (boolean check functions are okay)
|
|
80
84
|
if (functionName.startsWith('is'))
|
|
81
85
|
return;
|
|
86
|
+
// Skip Next.js data-fetching functions
|
|
87
|
+
if (NEXTJS_DATA_FUNCTIONS.has(functionName))
|
|
88
|
+
return;
|
|
82
89
|
// Extract first word from PascalCase/camelCase
|
|
83
90
|
let firstWord = functionName;
|
|
84
91
|
for (let i = 1; i < functionName.length; i++) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const syncOnwriteNameFunc: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"mismatchedName", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.syncOnwriteNameFunc = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.syncOnwriteNameFunc = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'sync-onwrite-name-func',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Ensure that the name field matches the func field in onWrite handlers',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
mismatchedName: 'The name field should match the func field in onWrite handlers',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
return {
|
|
23
|
+
ObjectExpression(node) {
|
|
24
|
+
let nameProperty;
|
|
25
|
+
let funcProperty;
|
|
26
|
+
for (const property of node.properties) {
|
|
27
|
+
if (property.type !== utils_1.AST_NODE_TYPES.Property)
|
|
28
|
+
continue;
|
|
29
|
+
if (property.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
30
|
+
property.key.name === 'name' &&
|
|
31
|
+
property.value.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
32
|
+
typeof property.value.value === 'string') {
|
|
33
|
+
nameProperty = property;
|
|
34
|
+
}
|
|
35
|
+
if (property.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
36
|
+
property.key.name === 'func') {
|
|
37
|
+
funcProperty = property;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!nameProperty || !funcProperty) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const nameValue = nameProperty.value.value;
|
|
44
|
+
let funcName;
|
|
45
|
+
// Handle variable references
|
|
46
|
+
if (funcProperty.value.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
47
|
+
const funcIdentifier = funcProperty.value;
|
|
48
|
+
const scope = context.getScope();
|
|
49
|
+
const variable = scope.references.find(ref => ref.identifier === funcIdentifier)?.resolved;
|
|
50
|
+
if (variable?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
51
|
+
variable.defs[0].node.init?.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
52
|
+
// If the variable is initialized with another identifier, use that name
|
|
53
|
+
funcName = variable.defs[0].node.init.name;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Otherwise use the variable name itself
|
|
57
|
+
funcName = funcIdentifier.name;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return; // Skip if func is not an identifier
|
|
62
|
+
}
|
|
63
|
+
if (nameValue !== funcName) {
|
|
64
|
+
const value = nameProperty.value;
|
|
65
|
+
if (value.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
66
|
+
context.report({
|
|
67
|
+
node: nameProperty,
|
|
68
|
+
messageId: 'mismatchedName',
|
|
69
|
+
fix(fixer) {
|
|
70
|
+
return fixer.replaceText(value, `'${funcName}'`);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=sync-onwrite-name-func.js.map
|