eslint-plugin-th-rules 3.5.0 → 3.6.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/dist/plugin.d.ts +6 -6
- package/dist/rules/no-destructuring.d.ts +8 -4
- package/dist/rules/no-destructuring.d.ts.map +1 -1
- package/dist/rules/no-destructuring.js +84 -15
- package/dist/rules/prefer-lodash-iteratee-shorthand.d.ts.map +1 -1
- package/dist/rules/prefer-lodash-iteratee-shorthand.js +117 -71
- package/package.json +1 -1
package/dist/plugin.d.ts
CHANGED
|
@@ -11,9 +11,9 @@ export declare const rules: {
|
|
|
11
11
|
'no-default-export': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
12
12
|
name: string;
|
|
13
13
|
};
|
|
14
|
-
'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
|
|
15
|
-
maximumDestructuredVariables
|
|
16
|
-
maximumLineLength
|
|
14
|
+
'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong" | "tooManyCumulative", [{
|
|
15
|
+
maximumDestructuredVariables?: number;
|
|
16
|
+
maximumLineLength?: number;
|
|
17
17
|
}], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
18
18
|
name: string;
|
|
19
19
|
};
|
|
@@ -63,9 +63,9 @@ declare const plugin: {
|
|
|
63
63
|
'no-default-export': import("@typescript-eslint/utils/ts-eslint").RuleModule<"unnamed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
64
64
|
name: string;
|
|
65
65
|
};
|
|
66
|
-
'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong", [{
|
|
67
|
-
maximumDestructuredVariables
|
|
68
|
-
maximumLineLength
|
|
66
|
+
'no-destructuring': import("@typescript-eslint/utils/ts-eslint").RuleModule<"tooDeep" | "tooMany" | "tooLong" | "tooManyCumulative", [{
|
|
67
|
+
maximumDestructuredVariables?: number;
|
|
68
|
+
maximumLineLength?: number;
|
|
69
69
|
}], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
70
70
|
name: string;
|
|
71
71
|
};
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
type Options = [
|
|
3
|
+
{
|
|
4
|
+
maximumDestructuredVariables?: number;
|
|
5
|
+
maximumLineLength?: number;
|
|
6
|
+
}
|
|
7
|
+
];
|
|
8
|
+
type MessageIds = 'tooDeep' | 'tooMany' | 'tooLong' | 'tooManyCumulative';
|
|
9
|
+
declare const noDestructuring: ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
|
|
6
10
|
name: string;
|
|
7
11
|
};
|
|
8
12
|
export default noDestructuring;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-destructuring.d.ts","sourceRoot":"","sources":["../../src/rules/no-destructuring.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAItF,QAAA,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"no-destructuring.d.ts","sourceRoot":"","sources":["../../src/rules/no-destructuring.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAItF,KAAK,OAAO,GAAG;IACd;QACC,4BAA4B,CAAC,EAAE,MAAM,CAAC;QACtC,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC3B;CACD,CAAC;AAEF,KAAK,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,CAAC;AAE1E,QAAA,MAAM,eAAe;;CA+NnB,CAAC;AAEH,eAAe,eAAe,CAAC"}
|
|
@@ -24,6 +24,7 @@ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
|
|
|
24
24
|
tooDeep: 'Destructuring at a nesting level above {{max}} is not allowed; found {{actual}} levels of nesting.',
|
|
25
25
|
tooMany: 'Destructuring of more than {{max}} variables is not allowed.',
|
|
26
26
|
tooLong: 'Destructuring spanning a line exceeding {{max}} characters is not allowed.',
|
|
27
|
+
tooManyCumulative: 'Destructuring of more than {{max}} variables from "{{source}}" in the same scope is not allowed; found {{total}}.',
|
|
27
28
|
},
|
|
28
29
|
},
|
|
29
30
|
defaultOptions: [
|
|
@@ -33,24 +34,62 @@ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
|
|
|
33
34
|
},
|
|
34
35
|
],
|
|
35
36
|
create(context, [options]) {
|
|
36
|
-
const
|
|
37
|
-
const
|
|
37
|
+
const maxVariables = options.maximumDestructuredVariables ?? 2;
|
|
38
|
+
const maxLineLength = options.maximumLineLength ?? 100;
|
|
39
|
+
/**
|
|
40
|
+
* Tracks total destructured properties per initializer expression text per scope node.
|
|
41
|
+
* WeakMap is used so scopes can be GC'ed and we do not leak memory across files.
|
|
42
|
+
*/
|
|
43
|
+
const totalsByScope = new WeakMap();
|
|
44
|
+
function getLineText(lineNumber) {
|
|
45
|
+
return context.sourceCode.lines[lineNumber - 1] ?? '';
|
|
46
|
+
}
|
|
47
|
+
function getMaxSpannedLineLength(startLine, endLine) {
|
|
48
|
+
let max = 0;
|
|
49
|
+
for (let i = startLine; i <= endLine; i++) {
|
|
50
|
+
const line = getLineText(i);
|
|
51
|
+
if (line.length > max) {
|
|
52
|
+
max = line.length;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return max;
|
|
56
|
+
}
|
|
57
|
+
function getIndentCountForLine(lineNumber) {
|
|
58
|
+
const lineText = getLineText(lineNumber);
|
|
59
|
+
return lineText.search(/\S|$/);
|
|
60
|
+
}
|
|
61
|
+
function getScopeNode(node) {
|
|
62
|
+
const ancestors = context.sourceCode.getAncestors(node);
|
|
63
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
64
|
+
const a = ancestors[i];
|
|
65
|
+
if (a.type === AST_NODE_TYPES.Program ||
|
|
66
|
+
a.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
67
|
+
a.type === AST_NODE_TYPES.FunctionExpression ||
|
|
68
|
+
a.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
69
|
+
a.type === AST_NODE_TYPES.TSDeclareFunction) {
|
|
70
|
+
return a;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return context.sourceCode.ast;
|
|
74
|
+
}
|
|
75
|
+
function getScopeMap(scopeNode) {
|
|
76
|
+
const existing = totalsByScope.get(scopeNode);
|
|
77
|
+
if (!_.isNil(existing)) {
|
|
78
|
+
return existing;
|
|
79
|
+
}
|
|
80
|
+
const created = new Map();
|
|
81
|
+
totalsByScope.set(scopeNode, created);
|
|
82
|
+
return created;
|
|
83
|
+
}
|
|
38
84
|
function reportIfNeeded(patternNode, reportNode = patternNode) {
|
|
39
85
|
if (patternNode?.type !== AST_NODE_TYPES.ObjectPattern || _.isNil(patternNode.loc)) {
|
|
40
86
|
return;
|
|
41
87
|
}
|
|
42
88
|
const startLine = patternNode.loc.start.line;
|
|
43
89
|
const endLine = patternNode.loc.end.line;
|
|
44
|
-
const
|
|
45
|
-
const indentCount = lineText.search(/\S|$/);
|
|
90
|
+
const indentCount = getIndentCountForLine(startLine);
|
|
46
91
|
const propertyCount = patternNode.properties?.length ?? 0;
|
|
47
|
-
|
|
48
|
-
for (let i = startLine; i <= endLine; i++) {
|
|
49
|
-
const t = context.sourceCode.lines[i - 1] ?? '';
|
|
50
|
-
if (t.length > maxSpannedLineLength) {
|
|
51
|
-
maxSpannedLineLength = t.length;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
92
|
+
const maxSpannedLineLength = getMaxSpannedLineLength(startLine, endLine);
|
|
54
93
|
if (indentCount > MAX_TAB_COUNT) {
|
|
55
94
|
context.report({
|
|
56
95
|
node: reportNode,
|
|
@@ -61,21 +100,21 @@ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
|
|
|
61
100
|
},
|
|
62
101
|
});
|
|
63
102
|
}
|
|
64
|
-
if (propertyCount >
|
|
103
|
+
if (propertyCount > maxVariables) {
|
|
65
104
|
context.report({
|
|
66
105
|
node: reportNode,
|
|
67
106
|
messageId: 'tooMany',
|
|
68
107
|
data: {
|
|
69
|
-
max:
|
|
108
|
+
max: maxVariables,
|
|
70
109
|
},
|
|
71
110
|
});
|
|
72
111
|
}
|
|
73
|
-
if (maxSpannedLineLength >
|
|
112
|
+
if (maxSpannedLineLength > maxLineLength) {
|
|
74
113
|
context.report({
|
|
75
114
|
node: reportNode,
|
|
76
115
|
messageId: 'tooLong',
|
|
77
116
|
data: {
|
|
78
|
-
max:
|
|
117
|
+
max: maxLineLength,
|
|
79
118
|
},
|
|
80
119
|
});
|
|
81
120
|
}
|
|
@@ -92,9 +131,39 @@ const noDestructuring = ESLintUtils.RuleCreator(() => 'https://github.com/tomerh
|
|
|
92
131
|
reportIfNeeded(p, p);
|
|
93
132
|
}
|
|
94
133
|
}
|
|
134
|
+
function checkCumulativeVariableDeclarator(node) {
|
|
135
|
+
if (node.id.type !== AST_NODE_TYPES.ObjectPattern) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (_.isNil(node.init)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const propertyCount = node.id.properties?.length ?? 0;
|
|
142
|
+
if (propertyCount > maxVariables) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const scopeNode = getScopeNode(node);
|
|
146
|
+
const scopeMap = getScopeMap(scopeNode);
|
|
147
|
+
const sourceText = context.sourceCode.getText(node.init);
|
|
148
|
+
const previousTotal = scopeMap.get(sourceText) ?? 0;
|
|
149
|
+
const newTotal = previousTotal + propertyCount;
|
|
150
|
+
scopeMap.set(sourceText, newTotal);
|
|
151
|
+
if (previousTotal > 0 && newTotal > maxVariables) {
|
|
152
|
+
context.report({
|
|
153
|
+
node,
|
|
154
|
+
messageId: 'tooManyCumulative',
|
|
155
|
+
data: {
|
|
156
|
+
source: sourceText,
|
|
157
|
+
max: maxVariables,
|
|
158
|
+
total: newTotal,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
95
163
|
return {
|
|
96
164
|
VariableDeclarator(node) {
|
|
97
165
|
reportIfNeeded(node.id, node);
|
|
166
|
+
checkCumulativeVariableDeclarator(node);
|
|
98
167
|
},
|
|
99
168
|
FunctionDeclaration(node) {
|
|
100
169
|
checkParameters(node.params);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-lodash-iteratee-shorthand.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-lodash-iteratee-shorthand.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,WAAW,EAAgC,MAAM,0BAA0B,CAAC;AAYrG,eAAO,MAAM,sBAAsB,6KAezB,CAAC;AAEX,eAAO,MAAM,qBAAqB,wPAsBxB,CAAC;AAEX,eAAO,MAAM,6BAA6B;;;;;;;;;;CAUhC,CAAC;AAUX,QAAA,MAAM,6BAA6B;;
|
|
1
|
+
{"version":3,"file":"prefer-lodash-iteratee-shorthand.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-lodash-iteratee-shorthand.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,WAAW,EAAgC,MAAM,0BAA0B,CAAC;AAYrG,eAAO,MAAM,sBAAsB,6KAezB,CAAC;AAEX,eAAO,MAAM,qBAAqB,wPAsBxB,CAAC;AAEX,eAAO,MAAM,6BAA6B;;;;;;;;;;CAUhC,CAAC;AAUX,QAAA,MAAM,6BAA6B;;CAmsBjC,CAAC;AAEH,eAAe,6BAA6B,CAAC"}
|
|
@@ -100,6 +100,9 @@ const preferLodashIterateeShorthand = ESLintUtils.RuleCreator(() => `https://git
|
|
|
100
100
|
function isStringLiteral(node) {
|
|
101
101
|
return !_.isNil(node) && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string';
|
|
102
102
|
}
|
|
103
|
+
function isNumberLiteral(node) {
|
|
104
|
+
return !_.isNil(node) && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'number';
|
|
105
|
+
}
|
|
103
106
|
function isTemplateLiteralWithoutExpressions(node) {
|
|
104
107
|
return !_.isNil(node) && node.type === AST_NODE_TYPES.TemplateLiteral && _.isEmpty(node.expressions) && node.quasis.length === 1;
|
|
105
108
|
}
|
|
@@ -154,57 +157,6 @@ const preferLodashIterateeShorthand = ESLintUtils.RuleCreator(() => `https://git
|
|
|
154
157
|
function isFunctionLike(node) {
|
|
155
158
|
return !_.isNil(node) && (node.type === AST_NODE_TYPES.ArrowFunctionExpression || node.type === AST_NODE_TYPES.FunctionExpression);
|
|
156
159
|
}
|
|
157
|
-
function isGetCallOnParameter(expr, parameterName) {
|
|
158
|
-
const unwrapped = unwrapChain(expr);
|
|
159
|
-
if (_.isNil(unwrapped) || unwrapped.type !== AST_NODE_TYPES.CallExpression)
|
|
160
|
-
return null;
|
|
161
|
-
const callee = unwrapChain(unwrapped.callee);
|
|
162
|
-
if (!isLodashMember(callee, 'get'))
|
|
163
|
-
return null;
|
|
164
|
-
if (unwrapped.arguments.length < 2)
|
|
165
|
-
return null;
|
|
166
|
-
const arg0 = unwrapped.arguments[0];
|
|
167
|
-
const arg1 = unwrapped.arguments[1];
|
|
168
|
-
if (_.isNil(arg0) || arg0.type === AST_NODE_TYPES.SpreadElement)
|
|
169
|
-
return null;
|
|
170
|
-
if (_.isNil(arg1) || arg1.type === AST_NODE_TYPES.SpreadElement)
|
|
171
|
-
return null;
|
|
172
|
-
const object = unwrapChain(arg0);
|
|
173
|
-
if (_.isNil(object) || object.type !== AST_NODE_TYPES.Identifier || object.name !== parameterName)
|
|
174
|
-
return null;
|
|
175
|
-
if (isStringLiteral(arg1)) {
|
|
176
|
-
return { kind: 'get', pathText: sourceCode.getText(arg1), pathIsStaticString: true };
|
|
177
|
-
}
|
|
178
|
-
if (isTemplateLiteralWithoutExpressions(arg1)) {
|
|
179
|
-
return { kind: 'get', pathText: sourceCode.getText(arg1), pathIsStaticString: true };
|
|
180
|
-
}
|
|
181
|
-
if (arg1.type === AST_NODE_TYPES.Identifier) {
|
|
182
|
-
return { kind: 'get', pathText: arg1.name, pathIsStaticString: false };
|
|
183
|
-
}
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
function isMemberAccessOnParameter(expr, parameterName) {
|
|
187
|
-
const unwrapped = unwrapChain(expr);
|
|
188
|
-
if (_.isNil(unwrapped) || unwrapped.type !== AST_NODE_TYPES.MemberExpression)
|
|
189
|
-
return null;
|
|
190
|
-
const object = unwrapChain(unwrapped.object);
|
|
191
|
-
if (_.isNil(object) || object.type !== AST_NODE_TYPES.Identifier || object.name !== parameterName)
|
|
192
|
-
return null;
|
|
193
|
-
if (!unwrapped.computed && unwrapped.property.type === AST_NODE_TYPES.Identifier) {
|
|
194
|
-
const key = unwrapped.property.name;
|
|
195
|
-
return { kind: 'member', keyText: key, keyIsIdentifier: true };
|
|
196
|
-
}
|
|
197
|
-
if (unwrapped.computed && unwrapped.property.type === AST_NODE_TYPES.Literal && typeof unwrapped.property.value === 'string') {
|
|
198
|
-
return { kind: 'member', keyText: sourceCode.getText(unwrapped.property), keyIsIdentifier: false };
|
|
199
|
-
}
|
|
200
|
-
if (unwrapped.computed && unwrapped.property.type === AST_NODE_TYPES.Identifier) {
|
|
201
|
-
return { kind: 'member', keyText: unwrapped.property.name, keyIsIdentifier: false };
|
|
202
|
-
}
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
function extractPathFromExpression(expr, parameterName) {
|
|
206
|
-
return isMemberAccessOnParameter(expr, parameterName) ?? isGetCallOnParameter(expr, parameterName);
|
|
207
|
-
}
|
|
208
160
|
function normalizeStaticPathText(pathText) {
|
|
209
161
|
const trimmed = pathText.trim();
|
|
210
162
|
if ((trimmed.startsWith("'") && trimmed.endsWith("'")) || (trimmed.startsWith('"') && trimmed.endsWith('"'))) {
|
|
@@ -222,6 +174,27 @@ const preferLodashIterateeShorthand = ESLintUtils.RuleCreator(() => `https://git
|
|
|
222
174
|
const escaped = value.replaceAll('\\', '\\\\').replaceAll("'", String.raw `\'`);
|
|
223
175
|
return `'${escaped}'`;
|
|
224
176
|
}
|
|
177
|
+
function toDoubleQuotedRaw(value) {
|
|
178
|
+
return value.replaceAll('\\', '\\\\').replaceAll('"', String.raw `\"`);
|
|
179
|
+
}
|
|
180
|
+
function buildLodashPathStringFromSegments(segments) {
|
|
181
|
+
if (_.isEmpty(segments))
|
|
182
|
+
return null;
|
|
183
|
+
let out = '';
|
|
184
|
+
for (const seg of segments) {
|
|
185
|
+
if (typeof seg === 'number') {
|
|
186
|
+
out += `[${seg}]`;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (isValidIdentifierName(seg)) {
|
|
190
|
+
out += _.isEmpty(out) ? seg : `.${seg}`;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const inner = toDoubleQuotedRaw(seg);
|
|
194
|
+
out += `["${inner}"]`;
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
225
198
|
function containsIdentifier(node, name) {
|
|
226
199
|
const { visitorKeys } = sourceCode;
|
|
227
200
|
const seen = new Set();
|
|
@@ -353,6 +326,80 @@ const preferLodashIterateeShorthand = ESLintUtils.RuleCreator(() => `https://git
|
|
|
353
326
|
return '{}';
|
|
354
327
|
return `{${props.join(', ')}}`;
|
|
355
328
|
}
|
|
329
|
+
function isGetCallOnParameter(expr, parameterName) {
|
|
330
|
+
const unwrapped = unwrapChain(expr);
|
|
331
|
+
if (_.isNil(unwrapped) || unwrapped.type !== AST_NODE_TYPES.CallExpression)
|
|
332
|
+
return null;
|
|
333
|
+
const callee = unwrapChain(unwrapped.callee);
|
|
334
|
+
if (!isLodashMember(callee, 'get'))
|
|
335
|
+
return null;
|
|
336
|
+
if (unwrapped.arguments.length < 2)
|
|
337
|
+
return null;
|
|
338
|
+
const arg0 = unwrapped.arguments[0];
|
|
339
|
+
const arg1 = unwrapped.arguments[1];
|
|
340
|
+
if (_.isNil(arg0) || arg0.type === AST_NODE_TYPES.SpreadElement)
|
|
341
|
+
return null;
|
|
342
|
+
if (_.isNil(arg1) || arg1.type === AST_NODE_TYPES.SpreadElement)
|
|
343
|
+
return null;
|
|
344
|
+
const object = unwrapChain(arg0);
|
|
345
|
+
if (_.isNil(object) || object.type !== AST_NODE_TYPES.Identifier || object.name !== parameterName)
|
|
346
|
+
return null;
|
|
347
|
+
if (isStringLiteral(arg1)) {
|
|
348
|
+
return { kind: 'get', pathText: sourceCode.getText(arg1), pathIsStaticString: true };
|
|
349
|
+
}
|
|
350
|
+
if (isTemplateLiteralWithoutExpressions(arg1)) {
|
|
351
|
+
return { kind: 'get', pathText: sourceCode.getText(arg1), pathIsStaticString: true };
|
|
352
|
+
}
|
|
353
|
+
if (arg1.type === AST_NODE_TYPES.Identifier) {
|
|
354
|
+
return { kind: 'get', pathText: arg1.name, pathIsStaticString: false };
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
function extractMemberPathSegments(expr, parameterName) {
|
|
359
|
+
const unwrapped = unwrapChainExpr(expr) ?? expr;
|
|
360
|
+
const segments = [];
|
|
361
|
+
let current = unwrapped;
|
|
362
|
+
while (current.type === AST_NODE_TYPES.MemberExpression) {
|
|
363
|
+
if (current.computed) {
|
|
364
|
+
const prop = unwrapChain(current.property);
|
|
365
|
+
if (!_.isNil(prop) && prop.type === AST_NODE_TYPES.Identifier)
|
|
366
|
+
return null;
|
|
367
|
+
if (isStringLiteral(prop)) {
|
|
368
|
+
segments.unshift(prop.value);
|
|
369
|
+
}
|
|
370
|
+
else if (isNumberLiteral(prop)) {
|
|
371
|
+
segments.unshift(prop.value);
|
|
372
|
+
}
|
|
373
|
+
else if (isTemplateLiteralWithoutExpressions(prop)) {
|
|
374
|
+
const cooked = prop.quasis[0]?.value.cooked ?? prop.quasis[0]?.value.raw ?? '';
|
|
375
|
+
segments.unshift(cooked);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
if (current.property.type !== AST_NODE_TYPES.Identifier)
|
|
383
|
+
return null;
|
|
384
|
+
segments.unshift(current.property.name);
|
|
385
|
+
}
|
|
386
|
+
const object = unwrapChain(current.object);
|
|
387
|
+
if (_.isNil(object))
|
|
388
|
+
return null;
|
|
389
|
+
current = object;
|
|
390
|
+
}
|
|
391
|
+
if (current.type !== AST_NODE_TYPES.Identifier || current.name !== parameterName)
|
|
392
|
+
return null;
|
|
393
|
+
if (_.isEmpty(segments))
|
|
394
|
+
return null;
|
|
395
|
+
return segments;
|
|
396
|
+
}
|
|
397
|
+
function extractPathFromExpression(expr, parameterName) {
|
|
398
|
+
const memberSegments = extractMemberPathSegments(expr, parameterName);
|
|
399
|
+
if (!_.isNil(memberSegments))
|
|
400
|
+
return { kind: 'memberPath', segments: memberSegments };
|
|
401
|
+
return isGetCallOnParameter(expr, parameterName);
|
|
402
|
+
}
|
|
356
403
|
function extractPredicateClausesFromExpression(expr, parameterName) {
|
|
357
404
|
const unwrapped = unwrapChainExpr(expr);
|
|
358
405
|
if (_.isNil(unwrapped))
|
|
@@ -392,21 +439,15 @@ const preferLodashIterateeShorthand = ESLintUtils.RuleCreator(() => `https://git
|
|
|
392
439
|
}
|
|
393
440
|
return clauses;
|
|
394
441
|
}
|
|
395
|
-
function buildObjectKeyTextForMemberPath(path) {
|
|
396
|
-
if (path.keyIsIdentifier) {
|
|
397
|
-
return path.keyText;
|
|
398
|
-
}
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
442
|
function buildMatchesObjectFromClauses(clauses) {
|
|
402
443
|
const literals = [];
|
|
403
444
|
for (const clause of clauses) {
|
|
404
445
|
const valueText = sourceCode.getText(clause.valueExpr);
|
|
405
|
-
if (clause.path.kind === '
|
|
406
|
-
const
|
|
407
|
-
if (_.isNil(
|
|
446
|
+
if (clause.path.kind === 'memberPath') {
|
|
447
|
+
const nested = buildNestedLiteralFromSegments(clause.path.segments, valueText);
|
|
448
|
+
if (_.isNil(nested))
|
|
408
449
|
return null;
|
|
409
|
-
literals.push(
|
|
450
|
+
literals.push(nested);
|
|
410
451
|
continue;
|
|
411
452
|
}
|
|
412
453
|
if (!clause.path.pathIsStaticString) {
|
|
@@ -430,19 +471,24 @@ const preferLodashIterateeShorthand = ESLintUtils.RuleCreator(() => `https://git
|
|
|
430
471
|
const expr = getReturnedExpression(fn);
|
|
431
472
|
if (_.isNil(expr))
|
|
432
473
|
return null;
|
|
474
|
+
const memberSegments = extractMemberPathSegments(expr, parameterName);
|
|
475
|
+
if (!_.isNil(memberSegments)) {
|
|
476
|
+
const raw = buildLodashPathStringFromSegments(memberSegments);
|
|
477
|
+
if (_.isNil(raw))
|
|
478
|
+
return null;
|
|
479
|
+
return toSingleQuotedStringLiteral(raw);
|
|
480
|
+
}
|
|
433
481
|
const unwrapped = unwrapChainExpr(expr) ?? expr;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (!
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
482
|
+
if (unwrapped.type === AST_NODE_TYPES.MemberExpression) {
|
|
483
|
+
const object = unwrapChain(unwrapped.object);
|
|
484
|
+
if (!_.isNil(object) && object.type === AST_NODE_TYPES.Identifier && object.name === parameterName && unwrapped.computed) {
|
|
485
|
+
const prop = unwrapChain(unwrapped.property);
|
|
486
|
+
if (!_.isNil(prop) && prop.type === AST_NODE_TYPES.Identifier) {
|
|
487
|
+
return prop.name;
|
|
488
|
+
}
|
|
441
489
|
}
|
|
442
|
-
const normalized = normalizeStaticPathText(member.keyText);
|
|
443
|
-
return toSingleQuotedStringLiteral(normalized);
|
|
444
490
|
}
|
|
445
|
-
const get = isGetCallOnParameter(
|
|
491
|
+
const get = isGetCallOnParameter(expr, parameterName);
|
|
446
492
|
if (!_.isNil(get) && get.kind === 'get') {
|
|
447
493
|
return get.pathText;
|
|
448
494
|
}
|