@blumintinc/eslint-plugin-blumint 1.1.9 → 1.2.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/README.md CHANGED
@@ -59,12 +59,14 @@ Or use the recommended config:
59
59
  | Name                                | Description | 💼 | ⚠️ | 🔧 |
60
60
  | :--------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :- | :- | :- |
61
61
  | [array-methods-this-context](docs/rules/array-methods-this-context.md) | Prevent misuse of Array methods in OOP | | ✅ | |
62
+ | [avoid-utils-directory](docs/rules/avoid-utils-directory.md) | Enforce using util/ instead of utils/ directory | ✅ | | 🔧 |
62
63
  | [class-methods-read-top-to-bottom](docs/rules/class-methods-read-top-to-bottom.md) | Ensures classes read linearly from top to bottom. | | ✅ | 🔧 |
63
64
  | [consistent-callback-naming](docs/rules/consistent-callback-naming.md) | Enforce consistent naming conventions for callback props and functions | ✅ | | 🔧 |
64
65
  | [dynamic-https-errors](docs/rules/dynamic-https-errors.md) | Dynamic error details should only be in the third argument of the HttpsError constructor. The second argument is hashed to produce a unique id. | | ✅ | |
65
66
  | [enforce-callable-types](docs/rules/enforce-callable-types.md) | Enforce Params and Response type exports in callable functions | ✅ | | |
66
67
  | [enforce-callback-memo](docs/rules/enforce-callback-memo.md) | Enforce useCallback or useMemo for inline functions in JSX props | ✅ | | |
67
68
  | [enforce-dynamic-firebase-imports](docs/rules/enforce-dynamic-firebase-imports.md) | Enforce dynamic importing for modules within the firebaseCloud directory | ✅ | | |
69
+ | [enforce-firestore-path-utils](docs/rules/enforce-firestore-path-utils.md) | Enforce usage of utility functions for Firestore paths | ✅ | | |
68
70
  | [enforce-identifiable-firestore-type](docs/rules/enforce-identifiable-firestore-type.md) | Enforce that Firestore type definitions extend Identifiable and match their folder name | ✅ | | |
69
71
  | [enforce-safe-stringify](docs/rules/enforce-safe-stringify.md) | Enforce using safe-stable-stringify instead of JSON.stringify | ✅ | | 🔧 |
70
72
  | [export-if-in-doubt](docs/rules/export-if-in-doubt.md) | All top-level const definitions, type definitions, and functions should be exported | | | |
@@ -74,6 +76,7 @@ Or use the recommended config:
74
76
  | [no-async-array-filter](docs/rules/no-async-array-filter.md) | Disallow async callbacks for Array.filter | ✅ | | |
75
77
  | [no-async-foreach](docs/rules/no-async-foreach.md) | Disallow Array.forEach with an async callback function | ✅ | | |
76
78
  | [no-conditional-literals-in-jsx](docs/rules/no-conditional-literals-in-jsx.md) | Disallow use of conditional literals in JSX code | ✅ | | |
79
+ | [no-entire-object-hook-deps](docs/rules/no-entire-object-hook-deps.md) | Avoid using entire objects in React hook dependency arrays when only specific fields are used | ✅ | | 🔧 |
77
80
  | [no-filter-without-return](docs/rules/no-filter-without-return.md) | Disallow Array.filter callbacks without an explicit return (if part of a block statement) | ✅ | | |
78
81
  | [no-jsx-whitespace-literal](docs/rules/no-jsx-whitespace-literal.md) | Disallow the use of {" "} elements in JSX code | ✅ | | |
79
82
  | [no-misused-switch-case](docs/rules/no-misused-switch-case.md) | Prevent misuse of logical OR in switch case statements | ✅ | | |
package/lib/index.js CHANGED
@@ -36,10 +36,12 @@ const enforce_safe_stringify_1 = require("./rules/enforce-safe-stringify");
36
36
  const avoid_utils_directory_1 = require("./rules/avoid-utils-directory");
37
37
  const no_entire_object_hook_deps_1 = require("./rules/no-entire-object-hook-deps");
38
38
  const enforce_firestore_path_utils_1 = require("./rules/enforce-firestore-path-utils");
39
+ const no_compositing_layer_props_1 = require("./rules/no-compositing-layer-props");
40
+ const enforce_firestore_doc_ref_generic_1 = require("./rules/enforce-firestore-doc-ref-generic");
39
41
  module.exports = {
40
42
  meta: {
41
43
  name: '@blumintinc/eslint-plugin-blumint',
42
- version: '1.1.9',
44
+ version: '1.2.1',
43
45
  },
44
46
  parseOptions: {
45
47
  ecmaVersion: 2020,
@@ -81,6 +83,8 @@ module.exports = {
81
83
  '@blumintinc/blumint/require-usememo-object-literals': 'error',
82
84
  '@blumintinc/blumint/enforce-safe-stringify': 'error',
83
85
  '@blumintinc/blumint/no-entire-object-hook-deps': 'error',
86
+ '@blumintinc/blumint/no-compositing-layer-props': 'warn',
87
+ '@blumintinc/blumint/enforce-firestore-doc-ref-generic': 'error',
84
88
  },
85
89
  },
86
90
  },
@@ -118,6 +122,8 @@ module.exports = {
118
122
  'avoid-utils-directory': avoid_utils_directory_1.avoidUtilsDirectory,
119
123
  'no-entire-object-hook-deps': no_entire_object_hook_deps_1.noEntireObjectHookDeps,
120
124
  'enforce-firestore-path-utils': enforce_firestore_path_utils_1.enforceFirestorePathUtils,
125
+ 'no-compositing-layer-props': no_compositing_layer_props_1.noCompositingLayerProps,
126
+ 'enforce-firestore-doc-ref-generic': enforce_firestore_doc_ref_generic_1.enforceFirestoreDocRefGeneric,
121
127
  },
122
128
  };
123
129
  //# sourceMappingURL=index.js.map
@@ -20,7 +20,7 @@ exports.dynamicHttpsErrors = (0, createRule_1.createRule)({
20
20
  type: 'suggestion',
21
21
  docs: {
22
22
  description: 'Dynamic error details should only be in the third argument of the HttpsError constructor. The second argument is hashed to produce a unique id.',
23
- recommended: 'warn',
23
+ recommended: 'error',
24
24
  },
25
25
  schema: [],
26
26
  messages: {
@@ -40,6 +40,19 @@ exports.default = (0, createRule_1.createRule)({
40
40
  }
41
41
  return false;
42
42
  }
43
+ function hasJSXWithFunction(node) {
44
+ if (node.type === utils_1.TSESTree.AST_NODE_TYPES.JSXElement) {
45
+ return node.openingElement.attributes.some((attr) => {
46
+ if (attr.type === utils_1.TSESTree.AST_NODE_TYPES.JSXAttribute && attr.value) {
47
+ if (attr.value.type === utils_1.TSESTree.AST_NODE_TYPES.JSXExpressionContainer) {
48
+ return containsFunction(attr.value.expression);
49
+ }
50
+ }
51
+ return false;
52
+ });
53
+ }
54
+ return false;
55
+ }
43
56
  function checkJSXAttribute(node) {
44
57
  if (!node.value ||
45
58
  node.value.type !== utils_1.TSESTree.AST_NODE_TYPES.JSXExpressionContainer) {
@@ -61,10 +74,15 @@ exports.default = (0, createRule_1.createRule)({
61
74
  });
62
75
  return;
63
76
  }
64
- // Check for objects/arrays containing functions
77
+ // Check for objects/arrays/JSX elements containing functions
65
78
  if ((expression.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression ||
66
- expression.type === utils_1.TSESTree.AST_NODE_TYPES.ArrayExpression) &&
67
- containsFunction(expression)) {
79
+ expression.type === utils_1.TSESTree.AST_NODE_TYPES.ArrayExpression ||
80
+ expression.type === utils_1.TSESTree.AST_NODE_TYPES.JSXElement) &&
81
+ (containsFunction(expression) || hasJSXWithFunction(expression))) {
82
+ // Skip reporting if this is a JSX element and we already reported an inline function
83
+ if (expression.type === utils_1.TSESTree.AST_NODE_TYPES.JSXElement && hasJSXWithFunction(expression)) {
84
+ return;
85
+ }
68
86
  context.report({
69
87
  node,
70
88
  messageId: 'enforceMemo',
@@ -0,0 +1,3 @@
1
+ type MessageIds = 'missingExportedType' | 'missingExportedReturnType' | 'missingExportedPropsType';
2
+ export declare const enforceExportedFunctionTypes: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
3
+ export {};
@@ -0,0 +1,249 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceExportedFunctionTypes = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
7
+ name: 'enforce-exported-function-types',
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Enforce exporting types for function props and return values',
12
+ recommended: 'error',
13
+ },
14
+ schema: [],
15
+ messages: {
16
+ missingExportedType: 'Type {{typeName}} should be exported since it is used in an exported function',
17
+ missingExportedReturnType: 'Return type {{typeName}} should be exported since it is used in an exported function',
18
+ missingExportedPropsType: 'Props type {{typeName}} should be exported since it is used in an exported React component',
19
+ },
20
+ },
21
+ defaultOptions: [],
22
+ create(context) {
23
+ const reportedTypes = new Set();
24
+ function isExported(node) {
25
+ if (!node)
26
+ return false;
27
+ if (node.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
28
+ node.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
29
+ return true;
30
+ }
31
+ const parent = node.parent;
32
+ if (!parent)
33
+ return false;
34
+ if (parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
35
+ parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration ||
36
+ parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator && isExported(parent.parent)) {
37
+ return true;
38
+ }
39
+ return false;
40
+ }
41
+ function getTypeName(node) {
42
+ if (!node)
43
+ return undefined;
44
+ switch (node.type) {
45
+ case utils_1.AST_NODE_TYPES.TSTypeReference:
46
+ if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
47
+ return node.typeName.name;
48
+ }
49
+ break;
50
+ case utils_1.AST_NODE_TYPES.TSTypeLiteral:
51
+ return 'AnonymousType';
52
+ }
53
+ return undefined;
54
+ }
55
+ function isTypeExported(typeName) {
56
+ const sourceCode = context.getSourceCode();
57
+ const program = sourceCode.ast;
58
+ // Check for exported type declarations
59
+ const exportedTypes = program.body.filter(node => {
60
+ if (node.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
61
+ if (node.declaration?.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
62
+ return node.declaration.id.name === typeName;
63
+ }
64
+ if (node.declaration?.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
65
+ return node.declaration.id.name === typeName;
66
+ }
67
+ }
68
+ return false;
69
+ });
70
+ if (exportedTypes.length > 0) {
71
+ return true;
72
+ }
73
+ // Check for type aliases in the current scope
74
+ const scope = context.getScope();
75
+ const variable = scope.variables.find(v => v.name === typeName);
76
+ if (!variable)
77
+ return false;
78
+ const def = variable.defs[0];
79
+ if (!def)
80
+ return false;
81
+ // Check if the type is directly exported
82
+ if (isExported(def.node))
83
+ return true;
84
+ // Check if the type is part of an export declaration
85
+ const parent = def.node.parent;
86
+ if (!parent)
87
+ return false;
88
+ // Handle type aliases in export declarations
89
+ if (parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
90
+ return true;
91
+ }
92
+ // Handle type aliases in variable declarations
93
+ if (parent.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
94
+ if (parent.parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
95
+ return true;
96
+ }
97
+ }
98
+ // Handle type aliases in interface declarations
99
+ if (parent.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
100
+ if (parent.parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) {
101
+ return true;
102
+ }
103
+ }
104
+ return false;
105
+ }
106
+ return {
107
+ FunctionDeclaration(node) {
108
+ if (!isExported(node))
109
+ return;
110
+ // Skip React components
111
+ if (node.id?.name && /^[A-Z]/.test(node.id.name))
112
+ return;
113
+ // Check return type
114
+ if (node.returnType?.typeAnnotation) {
115
+ const typeName = getTypeName(node.returnType.typeAnnotation);
116
+ if (typeName && !isTypeExported(typeName)) {
117
+ // Check if we've already reported this type
118
+ const key = `${typeName}-${node.loc?.start.line}-${node.loc?.start.column}`;
119
+ if (!reportedTypes.has(key)) {
120
+ reportedTypes.add(key);
121
+ context.report({
122
+ node: node.returnType,
123
+ messageId: 'missingExportedReturnType',
124
+ data: { typeName },
125
+ });
126
+ }
127
+ }
128
+ }
129
+ // Check parameter types
130
+ node.params.forEach(param => {
131
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
132
+ const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
133
+ if (typeName && !isTypeExported(typeName)) {
134
+ // Check if we've already reported this type
135
+ const key = `${typeName}-${param.loc?.start.line}-${param.loc?.start.column}`;
136
+ if (!reportedTypes.has(key)) {
137
+ reportedTypes.add(key);
138
+ context.report({
139
+ node: param.typeAnnotation,
140
+ messageId: 'missingExportedType',
141
+ data: { typeName },
142
+ });
143
+ }
144
+ }
145
+ }
146
+ });
147
+ },
148
+ 'VariableDeclarator > ArrowFunctionExpression'(node) {
149
+ if (!node.parent?.parent || !isExported(node.parent.parent))
150
+ return;
151
+ // Skip React components
152
+ if (node.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
153
+ node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier &&
154
+ /^[A-Z]/.test(node.parent.id.name))
155
+ return;
156
+ // Check return type
157
+ if (node.returnType?.typeAnnotation) {
158
+ const typeName = getTypeName(node.returnType.typeAnnotation);
159
+ if (typeName && !isTypeExported(typeName)) {
160
+ // Check if we've already reported this type
161
+ const key = `${typeName}-${node.loc?.start.line}-${node.loc?.start.column}`;
162
+ if (!reportedTypes.has(key)) {
163
+ reportedTypes.add(key);
164
+ context.report({
165
+ node: node.returnType,
166
+ messageId: 'missingExportedReturnType',
167
+ data: { typeName },
168
+ });
169
+ }
170
+ }
171
+ }
172
+ // Check parameter types
173
+ node.params.forEach(param => {
174
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation) {
175
+ const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
176
+ if (typeName && !isTypeExported(typeName)) {
177
+ // Check if we've already reported this type
178
+ const key = `${typeName}-${param.loc?.start.line}-${param.loc?.start.column}`;
179
+ if (!reportedTypes.has(key)) {
180
+ reportedTypes.add(key);
181
+ context.report({
182
+ node: param.typeAnnotation,
183
+ messageId: 'missingExportedType',
184
+ data: { typeName },
185
+ });
186
+ }
187
+ }
188
+ }
189
+ });
190
+ },
191
+ // Handle React components
192
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation]'(node) {
193
+ if (!isExported(node.parent))
194
+ return;
195
+ // Check props parameter
196
+ if (node.typeAnnotation) {
197
+ const typeName = getTypeName(node.typeAnnotation.typeAnnotation);
198
+ if (typeName && !isTypeExported(typeName)) {
199
+ // Check if we've already reported this type
200
+ const key = `${typeName}-${node.loc?.start.line}-${node.loc?.start.column}`;
201
+ if (!reportedTypes.has(key)) {
202
+ reportedTypes.add(key);
203
+ context.report({
204
+ node: node.typeAnnotation,
205
+ messageId: 'missingExportedType',
206
+ data: { typeName },
207
+ });
208
+ return;
209
+ }
210
+ }
211
+ }
212
+ },
213
+ // Skip type checking for React components since we handle them separately
214
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > TSTypeAnnotation'() { },
215
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > TSTypeAnnotation > TSTypeReference'() { },
216
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > TSTypeAnnotation'() { },
217
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > TSTypeAnnotation > TSTypeReference'() { },
218
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation'() { },
219
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation'() { },
220
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference'() { },
221
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference'() { },
222
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > Identifier'() { },
223
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > Identifier'() { },
224
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName'() { },
225
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName'() { },
226
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > Identifier'() { },
227
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > Identifier'() { },
228
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName'() { },
229
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName'() { },
230
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > Identifier'() { },
231
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > Identifier'() { },
232
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
233
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
234
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
235
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
236
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
237
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
238
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
239
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
240
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
241
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
242
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
243
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > Identifier'() { },
244
+ 'FunctionDeclaration[id.name=/^[A-Z]/] > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
245
+ 'VariableDeclarator[id.name=/^[A-Z]/] > ArrowFunctionExpression > Identifier[typeAnnotation] > TSTypeAnnotation > TSTypeReference > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName > TSQualifiedName'() { },
246
+ };
247
+ },
248
+ });
249
+ //# sourceMappingURL=enforce-exported-function-types.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview Enforce generic argument for Firestore DocumentReference
3
+ * @author BluMint
4
+ */
5
+ type MessageIds = 'missingGeneric' | 'invalidGeneric';
6
+ /**
7
+ * @type {import('eslint').Rule.RuleModule}
8
+ */
9
+ export declare const enforceFirestoreDocRefGeneric: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
10
+ export {};
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Enforce generic argument for Firestore DocumentReference
4
+ * @author BluMint
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.enforceFirestoreDocRefGeneric = void 0;
8
+ /**
9
+ * @type {import('eslint').Rule.RuleModule}
10
+ */
11
+ const utils_1 = require("@typescript-eslint/utils");
12
+ const createRule_1 = require("../utils/createRule");
13
+ /**
14
+ * @type {import('eslint').Rule.RuleModule}
15
+ */
16
+ exports.enforceFirestoreDocRefGeneric = (0, createRule_1.createRule)({
17
+ name: 'enforce-firestore-doc-ref-generic',
18
+ meta: {
19
+ type: 'problem',
20
+ docs: {
21
+ description: 'Enforce generic argument for Firestore DocumentReference',
22
+ recommended: 'error',
23
+ requiresTypeChecking: true,
24
+ },
25
+ schema: [],
26
+ messages: {
27
+ missingGeneric: 'DocumentReference must specify a generic type argument',
28
+ invalidGeneric: 'DocumentReference must not use "any" or "{}" as generic type argument',
29
+ },
30
+ },
31
+ defaultOptions: [],
32
+ create(context) {
33
+ const typeCache = new Map();
34
+ function hasInvalidType(node) {
35
+ if (!node)
36
+ return false;
37
+ switch (node.type) {
38
+ case utils_1.AST_NODE_TYPES.TSAnyKeyword:
39
+ return true;
40
+ case utils_1.AST_NODE_TYPES.TSTypeLiteral:
41
+ if (!node.members || node.members.length === 0) {
42
+ return true;
43
+ }
44
+ return node.members.some((member) => {
45
+ if (member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
46
+ member.typeAnnotation) {
47
+ return hasInvalidType(member.typeAnnotation.typeAnnotation);
48
+ }
49
+ return false;
50
+ });
51
+ case utils_1.AST_NODE_TYPES.TSTypeReference:
52
+ if (node.typeParameters) {
53
+ return node.typeParameters.params.some(hasInvalidType);
54
+ }
55
+ if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
56
+ const typeName = node.typeName.name;
57
+ if (typeCache.has(typeName)) {
58
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
59
+ return typeCache.get(typeName);
60
+ }
61
+ // Prevent infinite recursion
62
+ typeCache.set(typeName, false);
63
+ const program = context.getSourceCode().ast;
64
+ const interfaceDecl = program.body.find((n) => n.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration &&
65
+ n.id.name === typeName);
66
+ if (interfaceDecl) {
67
+ const result = interfaceDecl.body.body.some((member) => {
68
+ if (member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
69
+ member.typeAnnotation) {
70
+ return hasInvalidType(member.typeAnnotation.typeAnnotation);
71
+ }
72
+ return false;
73
+ });
74
+ typeCache.set(typeName, result);
75
+ return result;
76
+ }
77
+ }
78
+ return false;
79
+ case utils_1.AST_NODE_TYPES.TSIntersectionType:
80
+ case utils_1.AST_NODE_TYPES.TSUnionType:
81
+ return node.types.some(hasInvalidType);
82
+ case utils_1.AST_NODE_TYPES.TSTypeOperator:
83
+ if ('typeAnnotation' in node) {
84
+ return hasInvalidType(node.typeAnnotation);
85
+ }
86
+ return false;
87
+ case utils_1.AST_NODE_TYPES.TSMappedType:
88
+ if ('typeAnnotation' in node) {
89
+ return hasInvalidType(node.typeAnnotation);
90
+ }
91
+ return false;
92
+ case utils_1.AST_NODE_TYPES.TSIndexedAccessType:
93
+ return (hasInvalidType(node.objectType) || hasInvalidType(node.indexType));
94
+ case utils_1.AST_NODE_TYPES.TSConditionalType:
95
+ return (hasInvalidType(node.checkType) ||
96
+ hasInvalidType(node.extendsType) ||
97
+ hasInvalidType(node.trueType) ||
98
+ hasInvalidType(node.falseType));
99
+ case utils_1.AST_NODE_TYPES.TSArrayType:
100
+ return hasInvalidType(node.elementType);
101
+ case utils_1.AST_NODE_TYPES.TSTupleType:
102
+ return node.elementTypes.some(hasInvalidType);
103
+ case utils_1.AST_NODE_TYPES.TSTypeQuery:
104
+ return false;
105
+ default:
106
+ return false;
107
+ }
108
+ }
109
+ return {
110
+ TSTypeReference(node) {
111
+ if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
112
+ node.typeName.name === 'DocumentReference') {
113
+ // Check if generic type argument is missing
114
+ if (!node.typeParameters || node.typeParameters.params.length === 0) {
115
+ context.report({
116
+ node,
117
+ messageId: 'missingGeneric',
118
+ });
119
+ return;
120
+ }
121
+ // Check for invalid generic type arguments (any or {}) recursively
122
+ const typeArg = node.typeParameters.params[0];
123
+ if (hasInvalidType(typeArg)) {
124
+ context.report({
125
+ node,
126
+ messageId: 'invalidGeneric',
127
+ });
128
+ }
129
+ }
130
+ },
131
+ };
132
+ },
133
+ });
134
+ //# sourceMappingURL=enforce-firestore-doc-ref-generic.js.map
@@ -0,0 +1 @@
1
+ export declare const enforceMemoizeAsync: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"requireMemoize", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceMemoizeAsync = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
7
+ name: 'enforce-memoize-async',
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Enforce @Memoize() decorator on async methods with 0-1 parameters',
12
+ recommended: 'error',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ requireMemoize: 'Async methods with 0-1 parameters should be decorated with @Memoize()',
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ let hasMemoizeImport = false;
23
+ let memoizeAlias = 'Memoize';
24
+ return {
25
+ ImportDeclaration(node) {
26
+ if (node.source.value === 'typescript-memoize') {
27
+ const memoizeSpecifier = node.specifiers.find(spec => spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
28
+ spec.imported.name === 'Memoize');
29
+ if (memoizeSpecifier) {
30
+ hasMemoizeImport = true;
31
+ if (memoizeSpecifier.local) {
32
+ memoizeAlias = memoizeSpecifier.local.name;
33
+ }
34
+ }
35
+ }
36
+ },
37
+ MethodDefinition(node) {
38
+ // Only process async methods
39
+ if (node.value.type !== utils_1.AST_NODE_TYPES.FunctionExpression || !node.value.async) {
40
+ return;
41
+ }
42
+ // Skip methods with more than one parameter
43
+ if (node.value.params.length > 1) {
44
+ return;
45
+ }
46
+ // Check if method already has @Memoize decorator
47
+ const hasDecorator = node.decorators?.some(decorator => {
48
+ if (decorator.expression.type !== utils_1.AST_NODE_TYPES.CallExpression) {
49
+ return false;
50
+ }
51
+ const callee = decorator.expression.callee;
52
+ return (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
53
+ callee.name === memoizeAlias);
54
+ });
55
+ if (!hasDecorator && hasMemoizeImport) {
56
+ context.report({
57
+ node,
58
+ messageId: 'requireMemoize',
59
+ fix(fixer) {
60
+ // Add import if needed
61
+ // Add decorator
62
+ return fixer.insertTextBefore(node, `@${memoizeAlias}()\n${' '.repeat(node.loc.start.column)}`);
63
+ },
64
+ });
65
+ }
66
+ },
67
+ };
68
+ },
69
+ });
70
+ //# sourceMappingURL=enforce-memoize-async.js.map
@@ -9,7 +9,7 @@ exports.exportIfInDoubt = (0, createRule_1.createRule)({
9
9
  type: 'suggestion',
10
10
  docs: {
11
11
  description: 'All top-level const definitions, type definitions, and functions should be exported',
12
- recommended: 'warn',
12
+ recommended: 'error',
13
13
  },
14
14
  schema: [],
15
15
  messages: {
@@ -1,5 +1,3 @@
1
- import { TSESTree } from '@typescript-eslint/utils';
2
- declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"upperSnakeCase" | "asConst", never[], {
3
- VariableDeclaration(node: TSESTree.VariableDeclaration): void;
4
- }>;
1
+ type MessageIds = 'upperSnakeCase' | 'asConst';
2
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
5
3
  export default _default;