eslint-plugin-remeda 1.0.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/LICENSE +21 -0
- package/README.md +48 -0
- package/docs/rules/collection-method-value.md +27 -0
- package/docs/rules/collection-return.md +29 -0
- package/docs/rules/prefer-constant.md +53 -0
- package/docs/rules/prefer-do-nothing.md +29 -0
- package/docs/rules/prefer-filter.md +25 -0
- package/docs/rules/prefer-find.md +39 -0
- package/docs/rules/prefer-flat-map.md +25 -0
- package/docs/rules/prefer-is-empty.md +39 -0
- package/docs/rules/prefer-is-nil.md +27 -0
- package/docs/rules/prefer-map.md +29 -0
- package/docs/rules/prefer-nullish-coalescing.md +25 -0
- package/docs/rules/prefer-remeda-typecheck.md +36 -0
- package/docs/rules/prefer-some.md +29 -0
- package/docs/rules/prefer-times.md +36 -0
- package/package.json +62 -0
- package/src/index.js +35 -0
- package/src/rules/collection-method-value.js +82 -0
- package/src/rules/collection-return.js +71 -0
- package/src/rules/prefer-constant.js +87 -0
- package/src/rules/prefer-do-nothing.js +44 -0
- package/src/rules/prefer-filter.js +82 -0
- package/src/rules/prefer-find.js +68 -0
- package/src/rules/prefer-flat-map.js +50 -0
- package/src/rules/prefer-is-empty.js +134 -0
- package/src/rules/prefer-is-nil.js +97 -0
- package/src/rules/prefer-map.js +62 -0
- package/src/rules/prefer-nullish-coalescing.js +63 -0
- package/src/rules/prefer-remeda-typecheck.js +93 -0
- package/src/rules/prefer-some.js +43 -0
- package/src/rules/prefer-times.js +37 -0
- package/src/util/LodashContext.js +128 -0
- package/src/util/astUtil.js +353 -0
- package/src/util/getDocsUrl.js +17 -0
- package/src/util/importUtil.js +24 -0
- package/src/util/lodashUtil.js +123 -0
- package/src/util/methodData.js +2417 -0
- package/src/util/methodDataUtil.js +115 -0
- package/src/util/ruleUtil.js +13 -0
- package/src/util/settingsUtil.js +31 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to enforce usage of collection method values
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
schema: [],
|
16
|
+
docs: {
|
17
|
+
url: getDocsUrl("collection-method-value"),
|
18
|
+
},
|
19
|
+
},
|
20
|
+
|
21
|
+
create(context) {
|
22
|
+
const {
|
23
|
+
isChainBreaker,
|
24
|
+
getRemedaMethodVisitors,
|
25
|
+
isCallToMethod,
|
26
|
+
} = require("../util/lodashUtil");
|
27
|
+
const { getMethodName } = require("../util/astUtil");
|
28
|
+
const {
|
29
|
+
isCollectionMethod,
|
30
|
+
isAliasOfMethod,
|
31
|
+
getSideEffectIterationMethods,
|
32
|
+
} = require("../util/methodDataUtil");
|
33
|
+
const includes = require("lodash/includes");
|
34
|
+
|
35
|
+
function parentUsesValue(node, callType) {
|
36
|
+
const isBeforeChainBreaker =
|
37
|
+
callType === "chained" && isChainBreaker(node.parent.parent);
|
38
|
+
return (
|
39
|
+
(isBeforeChainBreaker ? node.parent.parent : node).parent.type !==
|
40
|
+
"ExpressionStatement"
|
41
|
+
);
|
42
|
+
}
|
43
|
+
|
44
|
+
function isPureRemedaCollectionMethod(method) {
|
45
|
+
return isCollectionMethod(method) && !isAliasOfMethod("remove", method);
|
46
|
+
}
|
47
|
+
|
48
|
+
function isSideEffectIterationMethod(method) {
|
49
|
+
return includes(getSideEffectIterationMethods(), method);
|
50
|
+
}
|
51
|
+
|
52
|
+
function isParentCommit(node, callType) {
|
53
|
+
return (
|
54
|
+
callType === "chained" && isCallToMethod(node.parent.parent, "commit")
|
55
|
+
);
|
56
|
+
}
|
57
|
+
|
58
|
+
return getRemedaMethodVisitors(
|
59
|
+
context,
|
60
|
+
(node, iteratee, { method, callType }) => {
|
61
|
+
if (
|
62
|
+
isPureRemedaCollectionMethod(method) &&
|
63
|
+
!parentUsesValue(node, callType)
|
64
|
+
) {
|
65
|
+
context.report({
|
66
|
+
node,
|
67
|
+
message: `Use value returned from R.${method}`,
|
68
|
+
});
|
69
|
+
} else if (
|
70
|
+
isSideEffectIterationMethod(method) &&
|
71
|
+
parentUsesValue(node, callType) &&
|
72
|
+
!isParentCommit(node, callType)
|
73
|
+
) {
|
74
|
+
context.report({
|
75
|
+
node,
|
76
|
+
message: `Do not use value returned from R.${getMethodName(node)}`,
|
77
|
+
});
|
78
|
+
}
|
79
|
+
},
|
80
|
+
);
|
81
|
+
},
|
82
|
+
};
|
@@ -0,0 +1,71 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to check that iteratees for all collection functions except forEach return a value;
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
schema: [],
|
16
|
+
docs: {
|
17
|
+
url: getDocsUrl("collection-return"),
|
18
|
+
},
|
19
|
+
},
|
20
|
+
|
21
|
+
create(context) {
|
22
|
+
const {
|
23
|
+
getRemedaMethodCallExpVisitor,
|
24
|
+
getLodashContext,
|
25
|
+
} = require("../util/lodashUtil");
|
26
|
+
const { isCollectionMethod } = require("../util/methodDataUtil");
|
27
|
+
const { isFunctionDefinitionWithBlock } = require("../util/astUtil");
|
28
|
+
const assign = require("lodash/assign");
|
29
|
+
const funcInfos = new Map();
|
30
|
+
let currFuncInfo = {};
|
31
|
+
const lodashContext = getLodashContext(context);
|
32
|
+
return assign(
|
33
|
+
{
|
34
|
+
"CallExpression:exit": getRemedaMethodCallExpVisitor(
|
35
|
+
lodashContext,
|
36
|
+
(node, iteratee, { method }) => {
|
37
|
+
if (isCollectionMethod(method) && funcInfos.has(iteratee)) {
|
38
|
+
const { hasReturn } = funcInfos.get(iteratee);
|
39
|
+
if (
|
40
|
+
isFunctionDefinitionWithBlock(iteratee) &&
|
41
|
+
!hasReturn &&
|
42
|
+
!iteratee.async &&
|
43
|
+
!iteratee.generator
|
44
|
+
) {
|
45
|
+
context.report({
|
46
|
+
node,
|
47
|
+
message: `Do not use R.${method} without returning a value`,
|
48
|
+
});
|
49
|
+
}
|
50
|
+
}
|
51
|
+
},
|
52
|
+
),
|
53
|
+
ReturnStatement() {
|
54
|
+
currFuncInfo.hasReturn = true;
|
55
|
+
},
|
56
|
+
onCodePathStart(codePath, node) {
|
57
|
+
currFuncInfo = {
|
58
|
+
upper: currFuncInfo,
|
59
|
+
codePath,
|
60
|
+
hasReturn: false,
|
61
|
+
};
|
62
|
+
funcInfos.set(node, currFuncInfo);
|
63
|
+
},
|
64
|
+
onCodePathEnd() {
|
65
|
+
currFuncInfo = currFuncInfo.upper;
|
66
|
+
},
|
67
|
+
},
|
68
|
+
lodashContext.getImportVisitors(),
|
69
|
+
);
|
70
|
+
},
|
71
|
+
};
|
@@ -0,0 +1,87 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to check if the expression could be better expressed as a R.constant
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
docs: {
|
16
|
+
url: getDocsUrl("prefer-constant"),
|
17
|
+
},
|
18
|
+
schema: [
|
19
|
+
{
|
20
|
+
type: "boolean",
|
21
|
+
},
|
22
|
+
{
|
23
|
+
type: "boolean",
|
24
|
+
},
|
25
|
+
],
|
26
|
+
},
|
27
|
+
|
28
|
+
create(context) {
|
29
|
+
const { getValueReturnedInFirstStatement } = require("../util/astUtil");
|
30
|
+
const shouldCheckArrowFunctions =
|
31
|
+
context.options[0] !== undefined ? context.options[0] : true;
|
32
|
+
const shouldCheckFunctionDeclarations =
|
33
|
+
context.options[1] !== undefined ? context.options[1] : false;
|
34
|
+
|
35
|
+
function isCompletelyLiteral(node) {
|
36
|
+
switch (node.type) {
|
37
|
+
case "Literal":
|
38
|
+
return true;
|
39
|
+
case "BinaryExpression":
|
40
|
+
return (
|
41
|
+
isCompletelyLiteral(node.left) && isCompletelyLiteral(node.right)
|
42
|
+
);
|
43
|
+
case "UnaryExpression":
|
44
|
+
return isCompletelyLiteral(node.argument);
|
45
|
+
case "ConditionalExpression":
|
46
|
+
return (
|
47
|
+
isCompletelyLiteral(node.test) &&
|
48
|
+
isCompletelyLiteral(node.consequent) &&
|
49
|
+
isCompletelyLiteral(node.alternate)
|
50
|
+
);
|
51
|
+
default:
|
52
|
+
return false;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
function reportIfLikeConstant(func, node) {
|
57
|
+
const valueReturnedInFirstLine = func(node);
|
58
|
+
if (
|
59
|
+
valueReturnedInFirstLine &&
|
60
|
+
isCompletelyLiteral(valueReturnedInFirstLine)
|
61
|
+
) {
|
62
|
+
context.report({
|
63
|
+
node,
|
64
|
+
message: "Prefer R.constant over a function returning a literal",
|
65
|
+
});
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
function handleFunctionDefinition(node) {
|
70
|
+
reportIfLikeConstant(getValueReturnedInFirstStatement, node);
|
71
|
+
}
|
72
|
+
|
73
|
+
return {
|
74
|
+
FunctionExpression: handleFunctionDefinition,
|
75
|
+
FunctionDeclaration(node) {
|
76
|
+
if (shouldCheckFunctionDeclarations) {
|
77
|
+
handleFunctionDefinition(node);
|
78
|
+
}
|
79
|
+
},
|
80
|
+
ArrowFunctionExpression(node) {
|
81
|
+
if (shouldCheckArrowFunctions) {
|
82
|
+
handleFunctionDefinition(node);
|
83
|
+
}
|
84
|
+
},
|
85
|
+
};
|
86
|
+
},
|
87
|
+
};
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to prefer R.doNothing() or R.constant(undefined) over an empty function
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
schema: [],
|
16
|
+
docs: {
|
17
|
+
url: getDocsUrl("prefer-do-nothing"),
|
18
|
+
},
|
19
|
+
},
|
20
|
+
|
21
|
+
create(context) {
|
22
|
+
const { getFirstFunctionLine } = require("../util/astUtil");
|
23
|
+
|
24
|
+
function reportIfEmptyFunction(node) {
|
25
|
+
if (
|
26
|
+
!getFirstFunctionLine(node) &&
|
27
|
+
node.parent.type !== "MethodDefinition" &&
|
28
|
+
!node.generator &&
|
29
|
+
!node.async
|
30
|
+
) {
|
31
|
+
context.report({
|
32
|
+
node,
|
33
|
+
message:
|
34
|
+
"Prefer R.doNothing() or R.constant(undefined) over an empty function",
|
35
|
+
});
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
return {
|
40
|
+
FunctionExpression: reportIfEmptyFunction,
|
41
|
+
ArrowFunctionExpression: reportIfEmptyFunction,
|
42
|
+
};
|
43
|
+
},
|
44
|
+
};
|
@@ -0,0 +1,82 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to check if a call to R.forEach should be a call to R.filter
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
docs: {
|
16
|
+
url: getDocsUrl("prefer-filter"),
|
17
|
+
},
|
18
|
+
schema: [
|
19
|
+
{
|
20
|
+
type: "integer",
|
21
|
+
},
|
22
|
+
],
|
23
|
+
},
|
24
|
+
|
25
|
+
create(context) {
|
26
|
+
const { getRemedaMethodVisitors } = require("../util/lodashUtil");
|
27
|
+
const {
|
28
|
+
isIdentifierWithName,
|
29
|
+
isMemberExpOf,
|
30
|
+
isNegationOfMemberOf,
|
31
|
+
isEqEqEqToMemberOf,
|
32
|
+
isNotEqEqToMemberOf,
|
33
|
+
getFirstFunctionLine,
|
34
|
+
hasOnlyOneStatement,
|
35
|
+
getFirstParamName,
|
36
|
+
} = require("../util/astUtil");
|
37
|
+
const { isAliasOfMethod } = require("../util/methodDataUtil");
|
38
|
+
const DEFAULT_MAX_PROPERTY_PATH_LENGTH = 3;
|
39
|
+
const maxLength =
|
40
|
+
parseInt(context.options[0], 10) || DEFAULT_MAX_PROPERTY_PATH_LENGTH;
|
41
|
+
|
42
|
+
function isIfWithoutElse(statement) {
|
43
|
+
return (
|
44
|
+
statement && statement.type === "IfStatement" && !statement.alternate
|
45
|
+
);
|
46
|
+
}
|
47
|
+
|
48
|
+
function canBeShorthand(exp, paramName) {
|
49
|
+
return (
|
50
|
+
isIdentifierWithName(exp, paramName) ||
|
51
|
+
isMemberExpOf(exp, paramName, { maxLength }) ||
|
52
|
+
isNegationOfMemberOf(exp, paramName, { maxLength }) ||
|
53
|
+
isEqEqEqToMemberOf(exp, paramName, { maxLength }) ||
|
54
|
+
isNotEqEqToMemberOf(exp, paramName, { maxLength })
|
55
|
+
);
|
56
|
+
}
|
57
|
+
|
58
|
+
function onlyHasSimplifiableIf(func) {
|
59
|
+
const firstLine = getFirstFunctionLine(func);
|
60
|
+
return (
|
61
|
+
func &&
|
62
|
+
hasOnlyOneStatement(func) &&
|
63
|
+
func.params.length === 1 &&
|
64
|
+
isIfWithoutElse(firstLine) &&
|
65
|
+
canBeShorthand(firstLine.test, getFirstParamName(func))
|
66
|
+
);
|
67
|
+
}
|
68
|
+
|
69
|
+
return getRemedaMethodVisitors(context, (node, iteratee, { method }) => {
|
70
|
+
if (
|
71
|
+
isAliasOfMethod("forEach", method) &&
|
72
|
+
onlyHasSimplifiableIf(iteratee)
|
73
|
+
) {
|
74
|
+
context.report({
|
75
|
+
node,
|
76
|
+
message:
|
77
|
+
"Prefer R.filter or R.some over an if statement inside a R.forEach",
|
78
|
+
});
|
79
|
+
}
|
80
|
+
});
|
81
|
+
},
|
82
|
+
};
|
@@ -0,0 +1,68 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to check if a call to `R.filter` should be a call to `R.find`.
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
schema: [],
|
16
|
+
docs: {
|
17
|
+
url: getDocsUrl("prefer-find"),
|
18
|
+
},
|
19
|
+
},
|
20
|
+
|
21
|
+
create(context) {
|
22
|
+
const {
|
23
|
+
getRemedaMethodVisitors,
|
24
|
+
isCallToMethod,
|
25
|
+
isCallToLodashMethod,
|
26
|
+
} = require("../util/lodashUtil");
|
27
|
+
const { isAliasOfMethod } = require("../util/methodDataUtil");
|
28
|
+
|
29
|
+
function isZeroIndexAccess(node) {
|
30
|
+
return node.type === "MemberExpression" && node.property.value === 0;
|
31
|
+
}
|
32
|
+
|
33
|
+
function isChainedBeforeMethod(callType, node, method) {
|
34
|
+
return (
|
35
|
+
callType === "chained" && isCallToMethod(node.parent.parent, method)
|
36
|
+
);
|
37
|
+
}
|
38
|
+
|
39
|
+
return getRemedaMethodVisitors(
|
40
|
+
context,
|
41
|
+
(node, iteratee, { method, callType, lodashContext }) => {
|
42
|
+
if (isAliasOfMethod("filter", method)) {
|
43
|
+
if (
|
44
|
+
isZeroIndexAccess(node.parent) ||
|
45
|
+
isCallToLodashMethod(node.parent, "first", lodashContext) ||
|
46
|
+
isChainedBeforeMethod(callType, node, "first")
|
47
|
+
) {
|
48
|
+
context.report({
|
49
|
+
node,
|
50
|
+
message:
|
51
|
+
"Prefer using `R.find` over selecting the first item of a filtered result",
|
52
|
+
});
|
53
|
+
}
|
54
|
+
if (
|
55
|
+
isCallToLodashMethod(node.parent, "last", lodashContext) ||
|
56
|
+
isChainedBeforeMethod(callType, node, "last")
|
57
|
+
) {
|
58
|
+
context.report({
|
59
|
+
node,
|
60
|
+
message:
|
61
|
+
"Prefer using `R.findLast` over selecting the last item of a filtered result",
|
62
|
+
});
|
63
|
+
}
|
64
|
+
}
|
65
|
+
},
|
66
|
+
);
|
67
|
+
},
|
68
|
+
};
|
@@ -0,0 +1,50 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to check if a call to map and flatten should be a call to R.flatMap
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
schema: [],
|
16
|
+
docs: {
|
17
|
+
url: getDocsUrl("prefer-flat-map"),
|
18
|
+
},
|
19
|
+
},
|
20
|
+
|
21
|
+
create(context) {
|
22
|
+
const {
|
23
|
+
getRemedaMethodVisitors,
|
24
|
+
isCallToMethod,
|
25
|
+
isCallToLodashMethod,
|
26
|
+
} = require("../util/lodashUtil");
|
27
|
+
const { getCaller } = require("../util/astUtil");
|
28
|
+
const { isAliasOfMethod } = require("../util/methodDataUtil");
|
29
|
+
|
30
|
+
function isChainedMapFlatten(callType, node) {
|
31
|
+
return callType === "chained" && isCallToMethod(getCaller(node), "map");
|
32
|
+
}
|
33
|
+
|
34
|
+
return getRemedaMethodVisitors(
|
35
|
+
context,
|
36
|
+
(node, iteratee, { method, callType, lodashContext }) => {
|
37
|
+
if (
|
38
|
+
isAliasOfMethod("flatten", method) &&
|
39
|
+
(isChainedMapFlatten(callType, node) ||
|
40
|
+
isCallToLodashMethod(node.arguments[0], "map", lodashContext))
|
41
|
+
) {
|
42
|
+
context.report({
|
43
|
+
node,
|
44
|
+
message: "Prefer R.flatMap over consecutive map and flatten.",
|
45
|
+
});
|
46
|
+
}
|
47
|
+
},
|
48
|
+
);
|
49
|
+
},
|
50
|
+
};
|
@@ -0,0 +1,134 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to prefer isEmpty over manually checking for length value.
|
3
|
+
*/
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
//------------------------------------------------------------------------------
|
7
|
+
// Rule Definition
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
|
10
|
+
const getDocsUrl = require("../util/getDocsUrl");
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
schema: [],
|
16
|
+
docs: {
|
17
|
+
url: getDocsUrl("prefer-is-empty"),
|
18
|
+
},
|
19
|
+
fixable: "code",
|
20
|
+
},
|
21
|
+
|
22
|
+
create(context) {
|
23
|
+
const { getLodashContext } = require("../util/lodashUtil");
|
24
|
+
const lodashContext = getLodashContext(context);
|
25
|
+
|
26
|
+
function getTextOfNode(node) {
|
27
|
+
if (node) {
|
28
|
+
if (node.type === "Identifier") {
|
29
|
+
return node.name;
|
30
|
+
}
|
31
|
+
return context.getSourceCode().getText(node);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
const visitors = lodashContext.getImportVisitors();
|
36
|
+
visitors.BinaryExpression = function (node) {
|
37
|
+
if (node.operator === "===") {
|
38
|
+
if (node.left) {
|
39
|
+
if (node.left.property && node.right) {
|
40
|
+
const leftExpressionMember = node.left.property.name;
|
41
|
+
const rightExpressionMember = node.right.value;
|
42
|
+
if (
|
43
|
+
leftExpressionMember === "length" &&
|
44
|
+
rightExpressionMember === 0
|
45
|
+
) {
|
46
|
+
const subjectObject = node.left.object;
|
47
|
+
context.report({
|
48
|
+
node,
|
49
|
+
message:
|
50
|
+
"Prefer isEmpty over manually checking for length value.",
|
51
|
+
fix(fixer) {
|
52
|
+
return fixer.replaceText(
|
53
|
+
node,
|
54
|
+
`isEmpty(${getTextOfNode(subjectObject)})`
|
55
|
+
);
|
56
|
+
},
|
57
|
+
});
|
58
|
+
}
|
59
|
+
} else if (
|
60
|
+
node.left.expression &&
|
61
|
+
node.right &&
|
62
|
+
node.left.expression.property
|
63
|
+
) {
|
64
|
+
const leftExpressionMember = node.left.expression.property.name;
|
65
|
+
const rightExpressionMember = node.right.value;
|
66
|
+
if (
|
67
|
+
leftExpressionMember === "length" &&
|
68
|
+
rightExpressionMember === 0
|
69
|
+
) {
|
70
|
+
const subjectObject = node.left.expression.object;
|
71
|
+
context.report({
|
72
|
+
node,
|
73
|
+
message:
|
74
|
+
"Prefer isEmpty over manually checking for length value.",
|
75
|
+
fix(fixer) {
|
76
|
+
return fixer.replaceText(
|
77
|
+
node,
|
78
|
+
`isEmpty(${getTextOfNode(subjectObject)})`
|
79
|
+
);
|
80
|
+
},
|
81
|
+
});
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
if (node.operator === ">") {
|
87
|
+
if (node.left) {
|
88
|
+
if (node.left.property && node.right) {
|
89
|
+
const leftExpressionMember = node.left.property.name;
|
90
|
+
const rightExpressionMember = node.right.value;
|
91
|
+
if (
|
92
|
+
leftExpressionMember === "length" &&
|
93
|
+
rightExpressionMember === 0
|
94
|
+
) {
|
95
|
+
const subjectObject = node.left.object;
|
96
|
+
context.report({
|
97
|
+
node,
|
98
|
+
message:
|
99
|
+
"Prefer isEmpty over manually checking for length value.",
|
100
|
+
fix(fixer) {
|
101
|
+
return fixer.replaceText(
|
102
|
+
node,
|
103
|
+
`!isEmpty(${getTextOfNode(subjectObject)})`
|
104
|
+
);
|
105
|
+
},
|
106
|
+
});
|
107
|
+
}
|
108
|
+
} else if (node.left.expression && node.right) {
|
109
|
+
const leftExpressionMember = node.left.expression.property.name;
|
110
|
+
const rightExpressionMember = node.right.value;
|
111
|
+
if (
|
112
|
+
leftExpressionMember === "length" &&
|
113
|
+
rightExpressionMember === 0
|
114
|
+
) {
|
115
|
+
const subjectObject = node.left.expression.object;
|
116
|
+
context.report({
|
117
|
+
node,
|
118
|
+
message:
|
119
|
+
"Prefer isEmpty over manually checking for length value.",
|
120
|
+
fix(fixer) {
|
121
|
+
return fixer.replaceText(
|
122
|
+
node,
|
123
|
+
`!isEmpty(${getTextOfNode(subjectObject)})`
|
124
|
+
);
|
125
|
+
},
|
126
|
+
});
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
};
|
132
|
+
return visitors;
|
133
|
+
},
|
134
|
+
};
|