@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 +3 -0
- package/lib/index.js +7 -1
- package/lib/rules/dynamic-https-errors.js +1 -1
- package/lib/rules/enforce-callback-memo.js +21 -3
- package/lib/rules/enforce-exported-function-types.d.ts +3 -0
- package/lib/rules/enforce-exported-function-types.js +249 -0
- package/lib/rules/enforce-firestore-doc-ref-generic.d.ts +10 -0
- package/lib/rules/enforce-firestore-doc-ref-generic.js +134 -0
- package/lib/rules/enforce-memoize-async.d.ts +1 -0
- package/lib/rules/enforce-memoize-async.js +70 -0
- package/lib/rules/export-if-in-doubt.js +1 -1
- package/lib/rules/global-const-style.d.ts +2 -4
- package/lib/rules/global-const-style.js +44 -22
- package/lib/rules/no-compositing-layer-props.d.ts +1 -0
- package/lib/rules/no-compositing-layer-props.js +161 -0
- package/lib/rules/no-entire-object-hook-deps.js +68 -60
- package/lib/rules/no-unused-props.js +33 -8
- package/lib/rules/no-useless-fragment.js +1 -1
- package/lib/rules/prefer-fragment-shorthand.js +1 -1
- package/lib/rules/prefer-settings-object.d.ts +11 -0
- package/lib/rules/prefer-settings-object.js +129 -0
- package/lib/rules/prefer-type-over-interface.js +1 -1
- package/lib/rules/require-https-error.js +32 -23
- package/lib/rules/require-image-overlayed.js +2 -0
- package/lib/rules/use-custom-memo.d.ts +1 -0
- package/lib/rules/use-custom-memo.js +65 -0
- package/lib/utils/ASTHelpers.js +14 -0
- package/package.json +1 -1
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
|
|
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: '
|
|
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
|
-
|
|
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: '
|
|
12
|
+
recommended: 'error',
|
|
13
13
|
},
|
|
14
14
|
schema: [],
|
|
15
15
|
messages: {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<
|
|
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;
|