@blumintinc/eslint-plugin-blumint 1.5.5 → 1.7.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.
Files changed (29) hide show
  1. package/README.md +62 -39
  2. package/lib/index.js +37 -1
  3. package/lib/rules/enforce-assert-throws.d.ts +1 -0
  4. package/lib/rules/enforce-assert-throws.js +132 -0
  5. package/lib/rules/enforce-centralized-mock-firestore.d.ts +1 -0
  6. package/lib/rules/enforce-centralized-mock-firestore.js +55 -0
  7. package/lib/rules/enforce-exported-function-types.js +3 -2
  8. package/lib/rules/enforce-firestore-facade.d.ts +3 -0
  9. package/lib/rules/enforce-firestore-facade.js +126 -0
  10. package/lib/rules/enforce-firestore-set-merge.js +83 -2
  11. package/lib/rules/enforce-verb-noun-naming.js +2 -0
  12. package/lib/rules/no-complex-cloud-params.d.ts +1 -0
  13. package/lib/rules/no-complex-cloud-params.js +363 -0
  14. package/lib/rules/no-explicit-return-type.js +11 -3
  15. package/lib/rules/no-firestore-jest-mock.d.ts +1 -0
  16. package/lib/rules/no-firestore-jest-mock.js +59 -0
  17. package/lib/rules/no-mixed-firestore-transactions.d.ts +1 -0
  18. package/lib/rules/no-mixed-firestore-transactions.js +115 -0
  19. package/lib/rules/no-mock-firebase-admin.d.ts +1 -0
  20. package/lib/rules/no-mock-firebase-admin.js +50 -0
  21. package/lib/rules/prefer-batch-operations.d.ts +3 -0
  22. package/lib/rules/prefer-batch-operations.js +176 -0
  23. package/lib/rules/prefer-clone-deep.d.ts +1 -0
  24. package/lib/rules/prefer-clone-deep.js +72 -0
  25. package/lib/rules/prefer-settings-object.js +164 -6
  26. package/lib/rules/semantic-function-prefixes.js +7 -0
  27. package/lib/rules/sync-onwrite-name-func.d.ts +1 -0
  28. package/lib/rules/sync-onwrite-name-func.js +79 -0
  29. package/package.json +2 -2
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noMixedFirestoreTransactions = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const NON_TRANSACTIONAL_CLASSES = new Set([
7
+ 'DocSetter',
8
+ 'FirestoreDocFetcher',
9
+ 'FirestoreFetcher',
10
+ ]);
11
+ exports.noMixedFirestoreTransactions = (0, createRule_1.createRule)({
12
+ name: 'no-mixed-firestore-transactions',
13
+ meta: {
14
+ type: 'problem',
15
+ docs: {
16
+ description: 'Prevent mixing transactional and non-transactional Firestore operations within a transaction',
17
+ recommended: 'error',
18
+ },
19
+ schema: [],
20
+ messages: {
21
+ noMixedTransactions: 'Do not use non-transactional Firestore operations ({{ className }}) inside a transaction. Use {{ transactionalClass }} instead.',
22
+ },
23
+ },
24
+ defaultOptions: [],
25
+ create(context) {
26
+ const transactionScopes = new Set();
27
+ function getTransactionalClassName(className) {
28
+ if (className === 'DocSetter')
29
+ return 'DocSetterTransaction';
30
+ if (className === 'FirestoreDocFetcher')
31
+ return 'FirestoreDocFetcherTransaction';
32
+ if (className === 'FirestoreFetcher')
33
+ return 'FirestoreFetcherTransaction';
34
+ return className;
35
+ }
36
+ function isFirestoreTransaction(node) {
37
+ const callee = node.callee;
38
+ if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression)
39
+ return false;
40
+ const property = callee.property;
41
+ return property.type === utils_1.AST_NODE_TYPES.Identifier && property.name === 'runTransaction';
42
+ }
43
+ function isNonTransactionalClass(node) {
44
+ const callee = node.callee;
45
+ if (callee.type !== utils_1.AST_NODE_TYPES.Identifier)
46
+ return false;
47
+ return NON_TRANSACTIONAL_CLASSES.has(callee.name);
48
+ }
49
+ function isTransactionParameter(param) {
50
+ if (param.type !== utils_1.AST_NODE_TYPES.Identifier)
51
+ return false;
52
+ const typeAnnotation = param.typeAnnotation;
53
+ if (!typeAnnotation || typeAnnotation.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation)
54
+ return false;
55
+ const type = typeAnnotation.typeAnnotation;
56
+ if (type.type !== utils_1.AST_NODE_TYPES.TSTypeReference)
57
+ return false;
58
+ const typeName = type.typeName;
59
+ if (typeName.type !== utils_1.AST_NODE_TYPES.TSQualifiedName)
60
+ return false;
61
+ return typeName.left.type === utils_1.AST_NODE_TYPES.Identifier && typeName.left.name === 'FirebaseFirestore' &&
62
+ typeName.right.type === utils_1.AST_NODE_TYPES.Identifier && typeName.right.name === 'Transaction';
63
+ }
64
+ function isInTransactionScope(node) {
65
+ let current = node;
66
+ while (current) {
67
+ if (transactionScopes.has(current)) {
68
+ return true;
69
+ }
70
+ if (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
71
+ current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
72
+ current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
73
+ const params = current.params;
74
+ if (params.some(isTransactionParameter)) {
75
+ return true;
76
+ }
77
+ }
78
+ current = current.parent;
79
+ }
80
+ return false;
81
+ }
82
+ return {
83
+ 'CallExpression[callee.property.name="runTransaction"]'(node) {
84
+ if (!isFirestoreTransaction(node))
85
+ return;
86
+ const callback = node.arguments[0];
87
+ if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
88
+ callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
89
+ transactionScopes.add(callback.body);
90
+ }
91
+ },
92
+ 'CallExpression[callee.property.name="runTransaction"]:exit'(node) {
93
+ const callback = node.arguments[0];
94
+ if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
95
+ callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
96
+ transactionScopes.delete(callback.body);
97
+ }
98
+ },
99
+ NewExpression(node) {
100
+ if (!isInTransactionScope(node) || !isNonTransactionalClass(node))
101
+ return;
102
+ const className = node.callee.name;
103
+ context.report({
104
+ node,
105
+ messageId: 'noMixedTransactions',
106
+ data: {
107
+ className,
108
+ transactionalClass: getTransactionalClassName(className),
109
+ },
110
+ });
111
+ },
112
+ };
113
+ },
114
+ });
115
+ //# sourceMappingURL=no-mixed-firestore-transactions.js.map
@@ -0,0 +1 @@
1
+ export declare const noMockFirebaseAdmin: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noMockFirebaseAdmin", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noMockFirebaseAdmin = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const FIREBASE_ADMIN_PATHS = [
7
+ '../../config/firebaseAdmin',
8
+ '../config/firebaseAdmin',
9
+ './config/firebaseAdmin',
10
+ 'functions/src/config/firebaseAdmin',
11
+ ];
12
+ exports.noMockFirebaseAdmin = (0, createRule_1.createRule)({
13
+ name: 'no-mock-firebase-admin',
14
+ meta: {
15
+ type: 'problem',
16
+ docs: {
17
+ description: 'Prevent mocking of functions/src/config/firebaseAdmin',
18
+ recommended: 'error',
19
+ },
20
+ schema: [],
21
+ messages: {
22
+ noMockFirebaseAdmin: 'Do not mock firebaseAdmin directly. Use mockFirestore from __mocks__/functions/src/config/mockFirestore instead.',
23
+ },
24
+ },
25
+ defaultOptions: [],
26
+ create(context) {
27
+ return {
28
+ CallExpression(node) {
29
+ if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
30
+ node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
31
+ node.callee.object.name === 'jest' &&
32
+ node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
33
+ node.callee.property.name === 'mock' &&
34
+ node.arguments.length > 0 &&
35
+ node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal &&
36
+ typeof node.arguments[0].value === 'string') {
37
+ const mockPath = node.arguments[0].value;
38
+ const isFirebaseAdminMock = FIREBASE_ADMIN_PATHS.some(path => mockPath.endsWith(path) || mockPath.includes('firebaseAdmin'));
39
+ if (isFirebaseAdminMock) {
40
+ context.report({
41
+ node,
42
+ messageId: 'noMockFirebaseAdmin',
43
+ });
44
+ }
45
+ }
46
+ },
47
+ };
48
+ },
49
+ });
50
+ //# sourceMappingURL=no-mock-firebase-admin.js.map
@@ -0,0 +1,3 @@
1
+ type MessageIds = 'preferSetAll' | 'preferOverwriteAll';
2
+ export declare const preferBatchOperations: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
3
+ export {};
@@ -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
@@ -0,0 +1 @@
1
+ export declare const preferCloneDeep: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"preferCloneDeep", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preferCloneDeep = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.preferCloneDeep = (0, createRule_1.createRule)({
7
+ name: 'prefer-clone-deep',
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Prefer using cloneDeep over nested spread copying',
12
+ recommended: 'error',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ preferCloneDeep: 'Use cloneDeep from functions/src/util/cloneDeep.ts instead of nested spread operators for deep object copying',
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ function hasNestedSpread(node) {
23
+ let spreadCount = 0;
24
+ let hasFunction = false;
25
+ let hasSymbol = false;
26
+ function visit(node) {
27
+ if (node.type === utils_1.AST_NODE_TYPES.SpreadElement) {
28
+ spreadCount++;
29
+ }
30
+ else if (node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
31
+ node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
32
+ hasFunction = true;
33
+ }
34
+ else if (node.type === utils_1.AST_NODE_TYPES.Property &&
35
+ node.computed &&
36
+ node.key.type === utils_1.AST_NODE_TYPES.Identifier &&
37
+ node.key.name === 'Symbol') {
38
+ hasSymbol = true;
39
+ }
40
+ for (const key in node) {
41
+ const value = node[key];
42
+ if (value && typeof value === 'object') {
43
+ visit(value);
44
+ }
45
+ }
46
+ }
47
+ visit(node);
48
+ return spreadCount > 1 && !hasFunction && !hasSymbol;
49
+ }
50
+ function getSourceText(node) {
51
+ return context.getSourceCode().getText(node);
52
+ }
53
+ function generateCloneDeepFix(node) {
54
+ const sourceText = getSourceText(node);
55
+ return `cloneDeep(${sourceText.replace(/\.\.\./g, '')}, {} as const)`;
56
+ }
57
+ return {
58
+ ObjectExpression(node) {
59
+ if (hasNestedSpread(node)) {
60
+ context.report({
61
+ node,
62
+ messageId: 'preferCloneDeep',
63
+ fix(fixer) {
64
+ return fixer.replaceText(node, generateCloneDeepFix(node));
65
+ },
66
+ });
67
+ }
68
+ },
69
+ };
70
+ },
71
+ });
72
+ //# sourceMappingURL=prefer-clone-deep.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,158 @@ 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
- typeMap.set(type, (typeMap.get(type) || 0) + 1);
76
- if (typeMap.get(type) > 1) {
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;
208
+ }
209
+ return false;
210
+ }
211
+ function hasABPattern(params) {
212
+ if (params.length !== 2)
213
+ return false;
214
+ const paramNames = params.map(param => {
215
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
216
+ return param.name;
217
+ }
218
+ return '';
219
+ });
220
+ // Check if both parameters end with 'A' and 'B' respectively and have the same prefix
221
+ const [first, second] = paramNames;
222
+ if (first.endsWith('A') && second.endsWith('B')) {
223
+ const firstPrefix = first.slice(0, -1);
224
+ const secondPrefix = second.slice(0, -1);
225
+ return firstPrefix === secondPrefix;
79
226
  }
80
227
  return false;
81
228
  }
82
229
  function shouldIgnoreNode(node) {
230
+ // Ignore built-in objects and third-party modules
231
+ if (isBuiltInOrThirdParty(node))
232
+ return true;
83
233
  // Ignore variadic functions if configured
84
234
  if (finalOptions.ignoreVariadicFunctions) {
85
235
  const hasRestParam = node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
86
- node.params.some(param => param.type === utils_1.AST_NODE_TYPES.RestElement);
236
+ node.params.some((param) => param.type === utils_1.AST_NODE_TYPES.RestElement);
87
237
  if (hasRestParam)
88
238
  return true;
89
239
  }
@@ -98,6 +248,16 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
98
248
  parent = parent.parent;
99
249
  }
100
250
  }
251
+ // Ignore functions with A/B pattern parameters
252
+ if ((node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
253
+ node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
254
+ node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
255
+ node.type === utils_1.AST_NODE_TYPES.TSMethodSignature ||
256
+ node.type === utils_1.AST_NODE_TYPES.TSFunctionType) &&
257
+ Array.isArray(node.params)) {
258
+ if (hasABPattern(node.params))
259
+ return true;
260
+ }
101
261
  return false;
102
262
  }
103
263
  function checkFunction(node) {
@@ -105,9 +265,7 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
105
265
  return;
106
266
  const params = node.params;
107
267
  // Check for too many parameters first
108
- const minParams = finalOptions.minimumParameters !== undefined
109
- ? finalOptions.minimumParameters
110
- : defaultOptions.minimumParameters;
268
+ const minParams = finalOptions.minimumParameters ?? defaultOptions.minimumParameters ?? 3;
111
269
  if (params.length >= minParams) {
112
270
  context.report({
113
271
  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>;