@blumintinc/eslint-plugin-blumint 1.2.0 → 1.3.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/README.md +40 -37
- package/lib/index.js +22 -13
- package/lib/rules/dynamic-https-errors.js +1 -1
- package/lib/rules/enforce-callback-memo.js +21 -3
- 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-firestore-set-merge.d.ts +1 -0
- package/lib/rules/enforce-firestore-set-merge.js +106 -0
- package/lib/rules/enforce-mock-firestore.d.ts +3 -0
- package/lib/rules/enforce-mock-firestore.js +76 -0
- package/lib/rules/enforce-verb-noun-naming.d.ts +1 -0
- package/lib/rules/enforce-verb-noun-naming.js +110 -0
- package/lib/rules/export-if-in-doubt.js +1 -1
- package/lib/rules/extract-global-constants.js +31 -2
- package/lib/rules/global-const-style.d.ts +2 -4
- package/lib/rules/global-const-style.js +48 -22
- package/lib/rules/no-compositing-layer-props.js +38 -4
- package/lib/rules/no-entire-object-hook-deps.js +68 -60
- package/lib/rules/no-explicit-return-type.d.ts +11 -0
- package/lib/rules/no-explicit-return-type.js +190 -0
- 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-type-over-interface.js +1 -1
- package/lib/rules/require-https-error.js +32 -23
- package/lib/rules/require-image-optimized.d.ts +7 -0
- package/lib/rules/require-image-optimized.js +82 -0
- package/lib/rules/semantic-function-prefixes.d.ts +1 -0
- package/lib/rules/semantic-function-prefixes.js +74 -0
- package/package.json +2 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noExplicitReturnType = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const defaultOptions = {
|
|
7
|
+
allowRecursiveFunctions: true,
|
|
8
|
+
allowOverloadedFunctions: true,
|
|
9
|
+
allowInterfaceMethodSignatures: true,
|
|
10
|
+
allowAbstractMethodSignatures: true,
|
|
11
|
+
allowDtsFiles: true,
|
|
12
|
+
};
|
|
13
|
+
function isRecursiveFunction(node) {
|
|
14
|
+
const functionName = node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ? node.id?.name :
|
|
15
|
+
node.type === utils_1.AST_NODE_TYPES.FunctionExpression && node.id ? node.id.name :
|
|
16
|
+
undefined;
|
|
17
|
+
if (!functionName || !node.body)
|
|
18
|
+
return false;
|
|
19
|
+
let hasRecursiveCall = false;
|
|
20
|
+
function checkNode(node) {
|
|
21
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
22
|
+
node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
23
|
+
node.callee.name === functionName) {
|
|
24
|
+
hasRecursiveCall = true;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Only traverse specific node types to avoid circular references
|
|
28
|
+
if (node.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
29
|
+
node.body.forEach(checkNode);
|
|
30
|
+
}
|
|
31
|
+
else if (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
|
|
32
|
+
checkNode(node.expression);
|
|
33
|
+
}
|
|
34
|
+
else if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
35
|
+
checkNode(node.callee);
|
|
36
|
+
node.arguments.forEach(checkNode);
|
|
37
|
+
}
|
|
38
|
+
else if (node.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
|
|
39
|
+
checkNode(node.left);
|
|
40
|
+
checkNode(node.right);
|
|
41
|
+
}
|
|
42
|
+
else if (node.type === utils_1.AST_NODE_TYPES.ReturnStatement && node.argument) {
|
|
43
|
+
checkNode(node.argument);
|
|
44
|
+
}
|
|
45
|
+
else if (node.type === utils_1.AST_NODE_TYPES.IfStatement) {
|
|
46
|
+
checkNode(node.test);
|
|
47
|
+
checkNode(node.consequent);
|
|
48
|
+
if (node.alternate) {
|
|
49
|
+
checkNode(node.alternate);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
checkNode(node.body);
|
|
54
|
+
return hasRecursiveCall;
|
|
55
|
+
}
|
|
56
|
+
function isOverloadedFunction(node) {
|
|
57
|
+
if (!node.parent)
|
|
58
|
+
return false;
|
|
59
|
+
if (node.type === utils_1.AST_NODE_TYPES.TSMethodSignature) {
|
|
60
|
+
const interfaceBody = node.parent;
|
|
61
|
+
if (interfaceBody.type !== utils_1.AST_NODE_TYPES.TSInterfaceBody)
|
|
62
|
+
return false;
|
|
63
|
+
const methodName = node.key.type === utils_1.AST_NODE_TYPES.Identifier ? node.key.name : undefined;
|
|
64
|
+
if (!methodName)
|
|
65
|
+
return false;
|
|
66
|
+
return interfaceBody.body.filter(member => member.type === utils_1.AST_NODE_TYPES.TSMethodSignature &&
|
|
67
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
68
|
+
member.key.name === methodName).length > 1;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
function isInterfaceOrAbstractMethodSignature(node) {
|
|
73
|
+
if (node.type === utils_1.AST_NODE_TYPES.TSMethodSignature)
|
|
74
|
+
return true;
|
|
75
|
+
if (node.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
|
|
76
|
+
let current = node;
|
|
77
|
+
while (current) {
|
|
78
|
+
if (current.type === utils_1.AST_NODE_TYPES.ClassDeclaration && current.abstract) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
current = current.parent;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
87
|
+
name: 'no-explicit-return-type',
|
|
88
|
+
meta: {
|
|
89
|
+
type: 'suggestion',
|
|
90
|
+
docs: {
|
|
91
|
+
description: 'Disallow explicit return types on functions',
|
|
92
|
+
recommended: 'error',
|
|
93
|
+
},
|
|
94
|
+
fixable: 'code',
|
|
95
|
+
schema: [
|
|
96
|
+
{
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
allowRecursiveFunctions: { type: 'boolean' },
|
|
100
|
+
allowOverloadedFunctions: { type: 'boolean' },
|
|
101
|
+
allowInterfaceMethodSignatures: { type: 'boolean' },
|
|
102
|
+
allowAbstractMethodSignatures: { type: 'boolean' },
|
|
103
|
+
allowDtsFiles: { type: 'boolean' },
|
|
104
|
+
},
|
|
105
|
+
additionalProperties: false,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
messages: {
|
|
109
|
+
noExplicitReturnType: 'Explicit return type is not allowed. Let TypeScript infer it.',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
defaultOptions: [defaultOptions],
|
|
113
|
+
create(context, [options]) {
|
|
114
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
115
|
+
const filename = context.getFilename();
|
|
116
|
+
if (mergedOptions.allowDtsFiles && filename.endsWith('.d.ts')) {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
function fixReturnType(fixer, node) {
|
|
120
|
+
const returnType = node.returnType || node.value?.returnType;
|
|
121
|
+
if (!returnType)
|
|
122
|
+
return null;
|
|
123
|
+
// Create a fix that removes the return type annotation
|
|
124
|
+
return fixer.remove(returnType);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
FunctionDeclaration(node) {
|
|
128
|
+
if (!node.returnType)
|
|
129
|
+
return;
|
|
130
|
+
if (mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
context.report({
|
|
134
|
+
node: node.returnType,
|
|
135
|
+
messageId: 'noExplicitReturnType',
|
|
136
|
+
fix: fixer => fixReturnType(fixer, node),
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
FunctionExpression(node) {
|
|
140
|
+
if (!node.returnType)
|
|
141
|
+
return;
|
|
142
|
+
if (mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
context.report({
|
|
146
|
+
node: node.returnType,
|
|
147
|
+
messageId: 'noExplicitReturnType',
|
|
148
|
+
fix: fixer => fixReturnType(fixer, node),
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
ArrowFunctionExpression(node) {
|
|
152
|
+
if (!node.returnType)
|
|
153
|
+
return;
|
|
154
|
+
context.report({
|
|
155
|
+
node: node.returnType,
|
|
156
|
+
messageId: 'noExplicitReturnType',
|
|
157
|
+
fix: fixer => fixReturnType(fixer, node),
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
TSMethodSignature(node) {
|
|
161
|
+
if (!node.returnType)
|
|
162
|
+
return;
|
|
163
|
+
if (mergedOptions.allowInterfaceMethodSignatures) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (mergedOptions.allowOverloadedFunctions && isOverloadedFunction(node)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
context.report({
|
|
170
|
+
node: node.returnType,
|
|
171
|
+
messageId: 'noExplicitReturnType',
|
|
172
|
+
fix: fixer => fixReturnType(fixer, node),
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
MethodDefinition(node) {
|
|
176
|
+
if (!node.value.returnType)
|
|
177
|
+
return;
|
|
178
|
+
if (mergedOptions.allowAbstractMethodSignatures && isInterfaceOrAbstractMethodSignature(node)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
context.report({
|
|
182
|
+
node: node.value.returnType,
|
|
183
|
+
messageId: 'noExplicitReturnType',
|
|
184
|
+
fix: fixer => fixReturnType(fixer, node),
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
//# sourceMappingURL=no-explicit-return-type.js.map
|
|
@@ -25,16 +25,28 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
25
25
|
return {
|
|
26
26
|
TSTypeAliasDeclaration(node) {
|
|
27
27
|
if (node.id.name.endsWith('Props')) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
member.
|
|
33
|
-
|
|
28
|
+
const props = {};
|
|
29
|
+
function extractProps(typeNode) {
|
|
30
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
31
|
+
typeNode.members.forEach((member) => {
|
|
32
|
+
if (member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
|
|
33
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
34
|
+
props[member.key.name] = member.key;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else if (typeNode.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
39
|
+
typeNode.types.forEach(extractProps);
|
|
40
|
+
}
|
|
41
|
+
else if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
42
|
+
// For referenced types like FormControlLabelProps, we need to track that these props should be forwarded
|
|
43
|
+
if (typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
44
|
+
props[`...${typeNode.typeName.name}`] = typeNode.typeName;
|
|
34
45
|
}
|
|
35
|
-
}
|
|
36
|
-
propsTypes.set(node.id.name, props);
|
|
46
|
+
}
|
|
37
47
|
}
|
|
48
|
+
extractProps(node.typeAnnotation);
|
|
49
|
+
propsTypes.set(node.id.name, props);
|
|
38
50
|
}
|
|
39
51
|
},
|
|
40
52
|
VariableDeclaration(node) {
|
|
@@ -56,6 +68,19 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
56
68
|
prop.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
57
69
|
used.add(prop.key.name);
|
|
58
70
|
}
|
|
71
|
+
else if (prop.type === utils_1.AST_NODE_TYPES.RestElement &&
|
|
72
|
+
prop.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
73
|
+
// Handle rest spread operator {...rest}
|
|
74
|
+
// When a rest operator is used, all remaining props are considered used
|
|
75
|
+
const propsType = propsTypes.get(typeName);
|
|
76
|
+
if (propsType) {
|
|
77
|
+
Object.keys(propsType).forEach((key) => {
|
|
78
|
+
if (key.startsWith('...')) {
|
|
79
|
+
used.add(key);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
59
84
|
});
|
|
60
85
|
usedProps.set(typeName, used);
|
|
61
86
|
}
|
|
@@ -35,7 +35,7 @@ exports.noUselessFragment = {
|
|
|
35
35
|
type: 'suggestion',
|
|
36
36
|
docs: {
|
|
37
37
|
description: 'Prevent unnecessary use of React fragments',
|
|
38
|
-
recommended: '
|
|
38
|
+
recommended: 'error',
|
|
39
39
|
},
|
|
40
40
|
messages: {
|
|
41
41
|
noUselessFragment: 'React fragment is unnecessary when wrapping a single child',
|
|
@@ -28,7 +28,7 @@ exports.preferFragmentShorthand = {
|
|
|
28
28
|
type: 'suggestion',
|
|
29
29
|
docs: {
|
|
30
30
|
description: 'Prefer <> shorthand for <React.Fragment>',
|
|
31
|
-
recommended: '
|
|
31
|
+
recommended: 'error',
|
|
32
32
|
},
|
|
33
33
|
messages: {
|
|
34
34
|
preferShorthand: 'Use <> shorthand for <React.Fragment>, unless a key is required for an iterator',
|
|
@@ -42,30 +42,39 @@ module.exports = (0, createRule_1.createRule)({
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
ThrowStatement(node) {
|
|
45
|
+
if (!node.argument) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
45
48
|
const argument = node.argument;
|
|
46
|
-
if (argument
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
if (argument.type !== utils_1.AST_NODE_TYPES.NewExpression) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const callee = argument.callee;
|
|
53
|
+
// Check for direct Error usage
|
|
54
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
55
|
+
callee.name === 'Error') {
|
|
56
|
+
context.report({
|
|
57
|
+
node,
|
|
58
|
+
messageId: 'useHttpsError',
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Check for firebase-admin HttpsError usage
|
|
63
|
+
if (!hasFirebaseAdminImport) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const isHttpsError = callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
67
|
+
callee.name === 'HttpsError';
|
|
68
|
+
const isFirebaseHttpsError = callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
69
|
+
callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
70
|
+
callee.object.name === httpsIdentifier &&
|
|
71
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
72
|
+
callee.property.name === 'HttpsError';
|
|
73
|
+
if (isHttpsError || isFirebaseHttpsError) {
|
|
74
|
+
context.report({
|
|
75
|
+
node,
|
|
76
|
+
messageId: 'useProprietaryHttpsError',
|
|
77
|
+
});
|
|
69
78
|
}
|
|
70
79
|
},
|
|
71
80
|
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const createRule_1 = require("../utils/createRule");
|
|
3
|
+
module.exports = (0, createRule_1.createRule)({
|
|
4
|
+
name: 'require-image-optimized',
|
|
5
|
+
meta: {
|
|
6
|
+
type: 'problem',
|
|
7
|
+
docs: {
|
|
8
|
+
description: 'Enforce using ImageOptimized component instead of next/image or img tags',
|
|
9
|
+
recommended: 'error',
|
|
10
|
+
requiresTypeChecking: false,
|
|
11
|
+
},
|
|
12
|
+
fixable: 'code',
|
|
13
|
+
schema: [
|
|
14
|
+
{
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
componentPath: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'The import path for the ImageOptimized component',
|
|
20
|
+
default: 'src/components/image/ImageOptimized',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
messages: {
|
|
27
|
+
useImageOptimized: 'Use ImageOptimized component from {{ componentPath }} instead of {{ component }}',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultOptions: [{ componentPath: 'src/components/image/ImageOptimized' }],
|
|
31
|
+
create(context) {
|
|
32
|
+
const options = context.options[0] || {
|
|
33
|
+
componentPath: 'src/components/image/ImageOptimized',
|
|
34
|
+
};
|
|
35
|
+
const sourceCode = context.getSourceCode();
|
|
36
|
+
return {
|
|
37
|
+
// Handle JSX img elements
|
|
38
|
+
JSXElement(node) {
|
|
39
|
+
if (node.openingElement.name.name === 'img') {
|
|
40
|
+
context.report({
|
|
41
|
+
node,
|
|
42
|
+
messageId: 'useImageOptimized',
|
|
43
|
+
data: {
|
|
44
|
+
componentPath: options.componentPath,
|
|
45
|
+
component: 'img tag',
|
|
46
|
+
},
|
|
47
|
+
fix(fixer) {
|
|
48
|
+
const attributes = node.openingElement.attributes
|
|
49
|
+
.map((attr) => sourceCode.getText(attr))
|
|
50
|
+
.join(' ');
|
|
51
|
+
return fixer.replaceText(node, `<ImageOptimized ${attributes} />`);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
// Handle next/image imports and usage
|
|
57
|
+
ImportDeclaration(node) {
|
|
58
|
+
if (node.source.value === 'next/image' && node.specifiers.length > 0) {
|
|
59
|
+
const imageSpecifier = node.specifiers.find((spec) => (spec.type === 'ImportDefaultSpecifier' ||
|
|
60
|
+
spec.type === 'ImportSpecifier') &&
|
|
61
|
+
(spec.local.name === 'Image' || spec.imported?.name === 'Image'));
|
|
62
|
+
if (imageSpecifier) {
|
|
63
|
+
const localName = imageSpecifier.local.name;
|
|
64
|
+
// Report the import
|
|
65
|
+
context.report({
|
|
66
|
+
node,
|
|
67
|
+
messageId: 'useImageOptimized',
|
|
68
|
+
data: {
|
|
69
|
+
componentPath: options.componentPath,
|
|
70
|
+
component: 'next/image',
|
|
71
|
+
},
|
|
72
|
+
fix(fixer) {
|
|
73
|
+
return fixer.replaceText(node, `import ${localName} from '${options.componentPath}';`);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=require-image-optimized.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const semanticFunctionPrefixes: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"avoidGenericPrefix", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.semanticFunctionPrefixes = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const DISALLOWED_PREFIXES = new Set(['get', 'update', 'check', 'manage', 'process', 'do']);
|
|
7
|
+
const SUGGESTED_ALTERNATIVES = {
|
|
8
|
+
get: ['fetch', 'retrieve', 'compute', 'derive'],
|
|
9
|
+
update: ['modify', 'set', 'apply'],
|
|
10
|
+
check: ['validate', 'assert', 'ensure'],
|
|
11
|
+
manage: ['control', 'coordinate', 'schedule'],
|
|
12
|
+
process: ['transform', 'sanitize', 'compute'],
|
|
13
|
+
do: ['execute', 'perform', 'apply'],
|
|
14
|
+
};
|
|
15
|
+
exports.semanticFunctionPrefixes = (0, createRule_1.createRule)({
|
|
16
|
+
name: 'semantic-function-prefixes',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'suggestion',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Enforce semantic function prefixes over generic ones like "get" and "update"',
|
|
21
|
+
recommended: 'error',
|
|
22
|
+
},
|
|
23
|
+
schema: [],
|
|
24
|
+
messages: {
|
|
25
|
+
avoidGenericPrefix: 'Avoid using generic prefix "{{prefix}}". Consider using one of these alternatives: {{alternatives}}',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
defaultOptions: [],
|
|
29
|
+
create(context) {
|
|
30
|
+
function checkFunctionName(node) {
|
|
31
|
+
// Skip anonymous functions
|
|
32
|
+
if (!node.id && node.parent?.type !== utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Get function name from either the function declaration or variable declarator
|
|
36
|
+
let functionName = '';
|
|
37
|
+
if (node.id) {
|
|
38
|
+
functionName = node.id.name;
|
|
39
|
+
}
|
|
40
|
+
else if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
41
|
+
functionName = node.parent.id.name;
|
|
42
|
+
}
|
|
43
|
+
if (!functionName)
|
|
44
|
+
return;
|
|
45
|
+
// Skip if function starts with 'is' (boolean check functions are okay)
|
|
46
|
+
if (functionName.startsWith('is'))
|
|
47
|
+
return;
|
|
48
|
+
// Skip class getters
|
|
49
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.MethodDefinition && node.parent.kind === 'get') {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Check for disallowed prefixes
|
|
53
|
+
for (const prefix of DISALLOWED_PREFIXES) {
|
|
54
|
+
if (functionName.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
55
|
+
context.report({
|
|
56
|
+
node: node.id || node,
|
|
57
|
+
messageId: 'avoidGenericPrefix',
|
|
58
|
+
data: {
|
|
59
|
+
prefix,
|
|
60
|
+
alternatives: SUGGESTED_ALTERNATIVES[prefix].join(', '),
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
FunctionDeclaration: checkFunctionName,
|
|
69
|
+
FunctionExpression: checkFunctionName,
|
|
70
|
+
ArrowFunctionExpression: checkFunctionName,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=semantic-function-prefixes.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blumintinc/eslint-plugin-blumint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Custom eslint rules for use within BluMint",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Brodie McGuire",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"commitlint": "commitlint --edit"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"compromise": "14.14.4",
|
|
48
49
|
"requireindex": "1.2.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|