eslint-plugin-ts-data-forge 0.8.5 → 0.9.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 +20 -19
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +1 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/rule-types.d.mts +16 -1
- package/dist/rule-types.d.mts.map +1 -1
- package/dist/rule-types.mjs.map +1 -1
- package/dist/rules/ast-utils.d.mts +21 -34
- package/dist/rules/ast-utils.d.mts.map +1 -1
- package/dist/rules/ast-utils.mjs +53 -58
- package/dist/rules/ast-utils.mjs.map +1 -1
- package/dist/rules/constants.d.mts +2 -0
- package/dist/rules/constants.d.mts.map +1 -1
- package/dist/rules/constants.mjs +9 -0
- package/dist/rules/constants.mjs.map +1 -1
- package/dist/rules/mutable-result.d.mts +10 -0
- package/dist/rules/mutable-result.d.mts.map +1 -0
- package/dist/rules/mutable-result.mjs +145 -0
- package/dist/rules/mutable-result.mjs.map +1 -0
- package/dist/rules/no-unnecessary-type-guard.d.mts.map +1 -1
- package/dist/rules/no-unnecessary-type-guard.mjs +6 -6
- package/dist/rules/no-unnecessary-type-guard.mjs.map +1 -1
- package/dist/rules/prefer-arr-generate.d.mts +57 -0
- package/dist/rules/prefer-arr-generate.d.mts.map +1 -0
- package/dist/rules/prefer-arr-generate.mjs +588 -0
- package/dist/rules/prefer-arr-generate.mjs.map +1 -0
- package/dist/rules/prefer-arr-is-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-array.mjs +7 -5
- package/dist/rules/prefer-arr-is-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs +9 -6
- package/dist/rules/prefer-arr-is-bounded-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs +9 -6
- package/dist/rules/prefer-arr-is-fixed-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-max-length-array.mjs +9 -6
- package/dist/rules/prefer-arr-is-max-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-min-length-array.mjs +9 -6
- package/dist/rules/prefer-arr-is-min-length-array.mjs.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.d.mts.map +1 -1
- package/dist/rules/prefer-arr-is-non-empty.mjs +9 -6
- package/dist/rules/prefer-arr-is-non-empty.mjs.map +1 -1
- package/dist/rules/prefer-arr-scan.d.mts.map +1 -1
- package/dist/rules/prefer-arr-scan.mjs +8 -11
- package/dist/rules/prefer-arr-scan.mjs.map +1 -1
- package/dist/rules/prefer-arr-sum.d.mts.map +1 -1
- package/dist/rules/prefer-arr-sum.mjs +8 -6
- package/dist/rules/prefer-arr-sum.mjs.map +1 -1
- package/dist/rules/prefer-arr-uniq.d.mts.map +1 -1
- package/dist/rules/prefer-arr-uniq.mjs +12 -166
- package/dist/rules/prefer-arr-uniq.mjs.map +1 -1
- package/dist/rules/prefer-as-int.d.mts.map +1 -1
- package/dist/rules/prefer-as-int.mjs +6 -8
- package/dist/rules/prefer-as-int.mjs.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-array-slicing.mjs +57 -34
- package/dist/rules/prefer-canonical-array-slicing.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-length-cast.mjs +3 -3
- package/dist/rules/prefer-canonical-length-cast.mjs.map +1 -1
- package/dist/rules/prefer-canonical-length-guard.d.mts.map +1 -1
- package/dist/rules/prefer-canonical-length-guard.mjs +4 -4
- package/dist/rules/prefer-canonical-length-guard.mjs.map +1 -1
- package/dist/rules/prefer-comparison-over-nullish-guard.d.mts.map +1 -1
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs +3 -2
- package/dist/rules/prefer-comparison-over-nullish-guard.mjs.map +1 -1
- package/dist/rules/prefer-is-non-null-object.d.mts.map +1 -1
- package/dist/rules/prefer-is-non-null-object.mjs +113 -43
- package/dist/rules/prefer-is-non-null-object.mjs.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.d.mts.map +1 -1
- package/dist/rules/prefer-is-record-and-has-key.mjs +105 -48
- package/dist/rules/prefer-is-record-and-has-key.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.d.mts.map +1 -1
- package/dist/rules/prefer-num-safe-parse-float.mjs +6 -6
- package/dist/rules/prefer-num-safe-parse-float.mjs.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.d.mts.map +1 -1
- package/dist/rules/prefer-num-safe-parse-int.mjs +6 -6
- package/dist/rules/prefer-num-safe-parse-int.mjs.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.d.mts.map +1 -1
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs +9 -12
- package/dist/rules/prefer-obj-over-entries-round-trip.mjs.map +1 -1
- package/dist/rules/prefer-range-for-loop.d.mts.map +1 -1
- package/dist/rules/prefer-range-for-loop.mjs +104 -23
- package/dist/rules/prefer-range-for-loop.mjs.map +1 -1
- package/dist/rules/rules.d.mts +1 -0
- package/dist/rules/rules.d.mts.map +1 -1
- package/dist/rules/rules.mjs +2 -0
- package/dist/rules/rules.mjs.map +1 -1
- package/package.json +11 -10
- package/src/plugin.mts +1 -0
- package/src/rule-types.mts +17 -1
- package/src/rules/ast-utils.mts +73 -80
- package/src/rules/constants.mts +12 -0
- package/src/rules/mutable-result.mts +204 -0
- package/src/rules/no-unnecessary-type-guard.mts +14 -10
- package/src/rules/prefer-arr-generate.mts +918 -0
- package/src/rules/prefer-arr-generate.test.mts +967 -0
- package/src/rules/prefer-arr-is-array.mts +14 -7
- package/src/rules/prefer-arr-is-array.test.mts +14 -0
- package/src/rules/prefer-arr-is-bounded-length-array.mts +16 -10
- package/src/rules/prefer-arr-is-bounded-length-array.test.mts +19 -0
- package/src/rules/prefer-arr-is-fixed-length-array.mts +17 -11
- package/src/rules/prefer-arr-is-max-length-array.mts +17 -11
- package/src/rules/prefer-arr-is-max-length-array.test.mts +8 -0
- package/src/rules/prefer-arr-is-min-length-array.mts +17 -11
- package/src/rules/prefer-arr-is-min-length-array.test.mts +8 -0
- package/src/rules/prefer-arr-is-non-empty.mts +16 -13
- package/src/rules/prefer-arr-scan.mts +24 -19
- package/src/rules/prefer-arr-sum.mts +18 -14
- package/src/rules/prefer-arr-uniq.mts +25 -234
- package/src/rules/prefer-as-int.mts +19 -12
- package/src/rules/prefer-canonical-array-slicing.mts +79 -52
- package/src/rules/prefer-canonical-array-slicing.test.mts +55 -0
- package/src/rules/prefer-canonical-length-cast.mts +7 -3
- package/src/rules/prefer-canonical-length-guard.mts +9 -4
- package/src/rules/prefer-comparison-over-nullish-guard.mts +3 -2
- package/src/rules/prefer-comparison-over-nullish-guard.test.mts +16 -0
- package/src/rules/prefer-is-non-null-object.mts +171 -64
- package/src/rules/prefer-is-non-null-object.test.mts +138 -0
- package/src/rules/prefer-is-record-and-has-key.mts +159 -74
- package/src/rules/prefer-is-record-and-has-key.test.mts +118 -23
- package/src/rules/prefer-num-safe-parse-float.mts +15 -10
- package/src/rules/prefer-num-safe-parse-int.mts +15 -13
- package/src/rules/prefer-obj-over-entries-round-trip.mts +19 -22
- package/src/rules/prefer-range-for-loop.mts +157 -31
- package/src/rules/prefer-range-for-loop.test.mts +127 -5
- package/src/rules/rules.mts +2 -0
- package/dist/rules/import-utils.d.mts +0 -30
- package/dist/rules/import-utils.d.mts.map +0 -1
- package/dist/rules/import-utils.mjs +0 -98
- package/dist/rules/import-utils.mjs.map +0 -1
- package/src/rules/import-utils.mts +0 -175
|
@@ -3,14 +3,16 @@ import {
|
|
|
3
3
|
type TSESLint,
|
|
4
4
|
type TSESTree,
|
|
5
5
|
} from '@typescript-eslint/utils';
|
|
6
|
-
import { Arr } from 'ts-data-forge';
|
|
7
|
-
import type { ReadonlyRecord } from 'ts-type-forge';
|
|
8
6
|
import {
|
|
9
|
-
|
|
7
|
+
buildNamedImportFix,
|
|
8
|
+
getArgumentText,
|
|
9
|
+
getImportDeclarations,
|
|
10
10
|
getImportedLocalName,
|
|
11
11
|
getNamedImports,
|
|
12
|
-
|
|
13
|
-
} from '
|
|
12
|
+
} from 'eslint-rule-forge';
|
|
13
|
+
import { Arr } from 'ts-data-forge';
|
|
14
|
+
import type { ReadonlyRecord } from 'ts-type-forge';
|
|
15
|
+
import { TS_DATA_FORGE_MODULE } from './constants.mjs';
|
|
14
16
|
|
|
15
17
|
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
|
|
16
18
|
|
|
@@ -91,7 +93,10 @@ export const preferObjOverEntriesRoundTrip: TSESLint.RuleModule<
|
|
|
91
93
|
|
|
92
94
|
const program = sourceCode.ast;
|
|
93
95
|
|
|
94
|
-
const
|
|
96
|
+
const tsDataForgeImports = getImportDeclarations(
|
|
97
|
+
program,
|
|
98
|
+
TS_DATA_FORGE_MODULE,
|
|
99
|
+
);
|
|
95
100
|
|
|
96
101
|
const mut_roundTrips: {
|
|
97
102
|
node: TSESTree.CallExpression;
|
|
@@ -146,7 +151,7 @@ export const preferObjOverEntriesRoundTrip: TSESLint.RuleModule<
|
|
|
146
151
|
body.test,
|
|
147
152
|
);
|
|
148
153
|
|
|
149
|
-
const someText = `${optionalName}.some(${
|
|
154
|
+
const someText = `${optionalName}.some(${getArgumentText(sourceCode, body.kept)})`;
|
|
150
155
|
|
|
151
156
|
const noneText = `${optionalName}.none`;
|
|
152
157
|
|
|
@@ -167,12 +172,13 @@ export const preferObjOverEntriesRoundTrip: TSESLint.RuleModule<
|
|
|
167
172
|
},
|
|
168
173
|
|
|
169
174
|
'Program:exit': () => {
|
|
170
|
-
const namedImports = getNamedImports(
|
|
175
|
+
const namedImports = getNamedImports(tsDataForgeImports);
|
|
171
176
|
|
|
172
|
-
const objName =
|
|
177
|
+
const objName =
|
|
178
|
+
getImportedLocalName(tsDataForgeImports, 'Obj') ?? 'Obj';
|
|
173
179
|
|
|
174
180
|
const optionalName =
|
|
175
|
-
getImportedLocalName(
|
|
181
|
+
getImportedLocalName(tsDataForgeImports, 'Optional') ?? 'Optional';
|
|
176
182
|
|
|
177
183
|
const needsOptional = mut_roundTrips.some(
|
|
178
184
|
({ roundTrip }) => roundTrip.body.kind === 'optional',
|
|
@@ -191,19 +197,16 @@ export const preferObjOverEntriesRoundTrip: TSESLint.RuleModule<
|
|
|
191
197
|
node,
|
|
192
198
|
messageId: MESSAGE_ID_OF[roundTrip.method],
|
|
193
199
|
fix: (fixer) => {
|
|
194
|
-
const recordText =
|
|
195
|
-
sourceCode.getText(roundTrip.record),
|
|
196
|
-
roundTrip.record,
|
|
197
|
-
);
|
|
200
|
+
const recordText = getArgumentText(sourceCode, roundTrip.record);
|
|
198
201
|
|
|
199
202
|
const replacement = `${objName}.${roundTrip.method}(${recordText}, ${buildParams(roundTrip)} => ${buildCallbackBody(roundTrip, optionalName)})`;
|
|
200
203
|
|
|
201
204
|
const importFixes =
|
|
202
205
|
index === 0 && missingImports.length > 0
|
|
203
|
-
?
|
|
206
|
+
? buildNamedImportFix(
|
|
204
207
|
fixer,
|
|
205
208
|
program,
|
|
206
|
-
|
|
209
|
+
TS_DATA_FORGE_MODULE,
|
|
207
210
|
missingImports,
|
|
208
211
|
)
|
|
209
212
|
: [];
|
|
@@ -551,12 +554,6 @@ const conditionalTestText = (
|
|
|
551
554
|
): string =>
|
|
552
555
|
NEEDS_PARENS_AS_TEST.has(node.type) ? (`(${text})` as const) : text;
|
|
553
556
|
|
|
554
|
-
/** A comma expression would read as another argument without parentheses. */
|
|
555
|
-
const argumentText = (text: string, node: TSESTree.Expression): string =>
|
|
556
|
-
node.type === AST_NODE_TYPES.SequenceExpression
|
|
557
|
-
? (`(${text})` as const)
|
|
558
|
-
: text;
|
|
559
|
-
|
|
560
557
|
/**
|
|
561
558
|
* A concise arrow body cannot start with `{`, nor hold a bare comma. The text
|
|
562
559
|
* is what decides the first case rather than the node type, because an object
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AST_NODE_TYPES,
|
|
3
|
+
ASTUtils,
|
|
3
4
|
type TSESLint,
|
|
4
5
|
type TSESTree,
|
|
5
6
|
} from '@typescript-eslint/utils';
|
|
6
|
-
import { Arr } from 'ts-data-forge';
|
|
7
|
-
import { skipTypeWrappers, toArgumentText } from './ast-utils.mjs';
|
|
8
7
|
import {
|
|
9
|
-
|
|
8
|
+
buildNamedImportFix,
|
|
9
|
+
getArgumentText,
|
|
10
|
+
getImportDeclarations,
|
|
10
11
|
getNamedImports,
|
|
11
|
-
|
|
12
|
-
} from '
|
|
12
|
+
skipTypeWrappers,
|
|
13
|
+
} from 'eslint-rule-forge';
|
|
14
|
+
import { Arr } from 'ts-data-forge';
|
|
15
|
+
import { TS_DATA_FORGE_MODULE } from './constants.mjs';
|
|
13
16
|
|
|
14
17
|
type Options = readonly [];
|
|
15
18
|
|
|
@@ -35,7 +38,44 @@ export const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
35
38
|
|
|
36
39
|
const program = sourceCode.ast;
|
|
37
40
|
|
|
38
|
-
const
|
|
41
|
+
const tsDataForgeImports = getImportDeclarations(
|
|
42
|
+
program,
|
|
43
|
+
TS_DATA_FORGE_MODULE,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Whether `node` is the same value on every iteration: a literal, or a
|
|
48
|
+
* variable that is never reassigned anywhere (not only in the loop, since
|
|
49
|
+
* a function the loop calls could reassign it). The loop reads its end and
|
|
50
|
+
* its step on every iteration, and `range` reads them once.
|
|
51
|
+
*/
|
|
52
|
+
const isStableBound = (
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
54
|
+
wrapped: TSESTree.Node,
|
|
55
|
+
): boolean => {
|
|
56
|
+
const node = skipTypeWrappers(wrapped);
|
|
57
|
+
|
|
58
|
+
if (node.type === AST_NODE_TYPES.Literal) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (node.type !== AST_NODE_TYPES.Identifier) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const variable = ASTUtils.findVariable(
|
|
67
|
+
sourceCode.getScope(node),
|
|
68
|
+
node.name,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
variable !== null &&
|
|
73
|
+
variable.defs.length > 0 &&
|
|
74
|
+
variable.references.every(
|
|
75
|
+
(reference) => !reference.isWrite() || reference.init === true,
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
};
|
|
39
79
|
|
|
40
80
|
const mut_nodesToFix: {
|
|
41
81
|
node: TSESTree.ForStatement;
|
|
@@ -43,6 +83,7 @@ export const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
43
83
|
begin: string;
|
|
44
84
|
end: string;
|
|
45
85
|
step?: string;
|
|
86
|
+
fixable: boolean;
|
|
46
87
|
}[] = [];
|
|
47
88
|
|
|
48
89
|
return {
|
|
@@ -69,6 +110,11 @@ export const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
69
110
|
|
|
70
111
|
const beginExpr = declaration.init;
|
|
71
112
|
|
|
113
|
+
// `range` takes safe integers only.
|
|
114
|
+
if (isNonIntegerNumberLiteral(beginExpr)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
72
118
|
// Check test: i < end. The loop variable and the step are read
|
|
73
119
|
// through type wrappers: `(i satisfies number) < n` tests `i`, and
|
|
74
120
|
// `i += 0 as number` still steps by 0.
|
|
@@ -121,22 +167,38 @@ export const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
121
167
|
const stepNum = stepValue.value;
|
|
122
168
|
|
|
123
169
|
// Only support positive integer steps
|
|
124
|
-
if (
|
|
170
|
+
if (
|
|
171
|
+
typeof stepNum === 'number' &&
|
|
172
|
+
Number.isSafeInteger(stepNum) &&
|
|
173
|
+
stepNum > 0
|
|
174
|
+
) {
|
|
125
175
|
mut_step = stepNum === 1 ? undefined : stepNum.toString();
|
|
126
176
|
} else {
|
|
127
177
|
return;
|
|
128
178
|
}
|
|
179
|
+
} else if (isNegatedLiteral(stepValue)) {
|
|
180
|
+
// `i += -1` under `i < end` never gets there, and `range` with a
|
|
181
|
+
// negative step yields nothing: no fix that type-checks would
|
|
182
|
+
// keep that.
|
|
183
|
+
return;
|
|
129
184
|
} else {
|
|
130
|
-
// Variable step like i += step
|
|
131
|
-
|
|
185
|
+
// Variable step like i += step. Its type is left to the type
|
|
186
|
+
// checker: a step that is not a non-zero safe integer makes the
|
|
187
|
+
// fixed `range(...)` call fail to type-check.
|
|
188
|
+
mut_step = getArgumentText(sourceCode, node.update.right);
|
|
132
189
|
}
|
|
133
190
|
} else {
|
|
134
191
|
return;
|
|
135
192
|
}
|
|
136
193
|
|
|
137
|
-
const
|
|
194
|
+
// `const i` would not take an assignment anywhere but the update.
|
|
195
|
+
if (isAssignedOutsideUpdate(sourceCode, declaration, node.update)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const beginText = getArgumentText(sourceCode, beginExpr);
|
|
138
200
|
|
|
139
|
-
const endText =
|
|
201
|
+
const endText = getArgumentText(sourceCode, endExpr);
|
|
140
202
|
|
|
141
203
|
mut_nodesToFix.push({
|
|
142
204
|
node,
|
|
@@ -144,17 +206,22 @@ export const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
144
206
|
begin: beginText,
|
|
145
207
|
end: endText,
|
|
146
208
|
step: mut_step,
|
|
209
|
+
fixable:
|
|
210
|
+
isStableBound(endExpr) &&
|
|
211
|
+
(node.update.type !== AST_NODE_TYPES.AssignmentExpression ||
|
|
212
|
+
isStableBound(node.update.right)),
|
|
147
213
|
});
|
|
148
214
|
},
|
|
149
215
|
'Program:exit': () => {
|
|
150
|
-
const namedImports = getNamedImports(
|
|
216
|
+
const namedImports = getNamedImports(tsDataForgeImports);
|
|
151
217
|
|
|
152
218
|
const hasRangeImport = namedImports.includes('range');
|
|
153
219
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
220
|
+
const importCarrier = mut_nodesToFix.find(({ fixable }) => fixable);
|
|
221
|
+
|
|
222
|
+
for (const entry of mut_nodesToFix) {
|
|
223
|
+
const { node, varName, begin, end, step } = entry;
|
|
224
|
+
|
|
158
225
|
context.report({
|
|
159
226
|
node: node.init ?? node,
|
|
160
227
|
messageId: 'useRangeForLoop',
|
|
@@ -163,25 +230,33 @@ export const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
163
230
|
begin,
|
|
164
231
|
end,
|
|
165
232
|
},
|
|
166
|
-
fix:
|
|
167
|
-
|
|
233
|
+
fix: entry.fixable
|
|
234
|
+
? (fixer) => {
|
|
235
|
+
const bodyText = sourceCode.getText(node.body);
|
|
168
236
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
237
|
+
const rangeArgs =
|
|
238
|
+
step === undefined
|
|
239
|
+
? `${begin}, ${end}`
|
|
240
|
+
: `${begin}, ${end}, ${step}`;
|
|
173
241
|
|
|
174
|
-
|
|
242
|
+
const replacement = `for (const ${varName} of range(${rangeArgs})) ${bodyText}`;
|
|
175
243
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
244
|
+
// The import fix lives on the first fixable report:
|
|
245
|
+
// `insertTextBefore(program, …)` produces overlapping ranges
|
|
246
|
+
// otherwise.
|
|
247
|
+
const importFixes =
|
|
248
|
+
entry === importCarrier && !hasRangeImport
|
|
249
|
+
? buildNamedImportFix(
|
|
250
|
+
fixer,
|
|
251
|
+
program,
|
|
252
|
+
TS_DATA_FORGE_MODULE,
|
|
253
|
+
['range'],
|
|
254
|
+
)
|
|
255
|
+
: [];
|
|
182
256
|
|
|
183
|
-
|
|
184
|
-
|
|
257
|
+
return [...importFixes, fixer.replaceText(node, replacement)];
|
|
258
|
+
}
|
|
259
|
+
: undefined,
|
|
185
260
|
});
|
|
186
261
|
}
|
|
187
262
|
},
|
|
@@ -189,3 +264,54 @@ export const preferRangeForLoop: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
189
264
|
},
|
|
190
265
|
defaultOptions: [],
|
|
191
266
|
} as const;
|
|
267
|
+
|
|
268
|
+
const isNegatedLiteral = (
|
|
269
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
270
|
+
node: TSESTree.Node,
|
|
271
|
+
): boolean =>
|
|
272
|
+
node.type === AST_NODE_TYPES.UnaryExpression &&
|
|
273
|
+
node.operator === '-' &&
|
|
274
|
+
skipTypeWrappers(node.argument).type === AST_NODE_TYPES.Literal;
|
|
275
|
+
|
|
276
|
+
const isNonIntegerNumberLiteral = (
|
|
277
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
278
|
+
wrapped: TSESTree.Node,
|
|
279
|
+
): boolean => {
|
|
280
|
+
const node = skipTypeWrappers(wrapped);
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
node.type === AST_NODE_TYPES.Literal &&
|
|
284
|
+
typeof node.value === 'number' &&
|
|
285
|
+
!Number.isSafeInteger(node.value)
|
|
286
|
+
);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Whether the loop variable declared by `declaration` is written anywhere but
|
|
291
|
+
* its initializer and the loop's `update`.
|
|
292
|
+
*/
|
|
293
|
+
const isAssignedOutsideUpdate = (
|
|
294
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
295
|
+
sourceCode: TSESLint.SourceCode,
|
|
296
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
297
|
+
declaration: TSESTree.VariableDeclarator,
|
|
298
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
299
|
+
update: TSESTree.Expression,
|
|
300
|
+
): boolean =>
|
|
301
|
+
sourceCode
|
|
302
|
+
.getDeclaredVariables(declaration)
|
|
303
|
+
.flatMap((variable) => variable.references)
|
|
304
|
+
.some(
|
|
305
|
+
(reference) =>
|
|
306
|
+
reference.isWrite() &&
|
|
307
|
+
reference.init !== true &&
|
|
308
|
+
!isWithin(reference.identifier, update),
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const isWithin = (
|
|
312
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
313
|
+
node: TSESTree.Node,
|
|
314
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
315
|
+
container: TSESTree.Node,
|
|
316
|
+
): boolean =>
|
|
317
|
+
container.range[0] <= node.range[0] && node.range[1] <= container.range[1];
|
|
@@ -205,22 +205,20 @@ describe('prefer-range-for-loop with parenthesized operands', () => {
|
|
|
205
205
|
valid: [],
|
|
206
206
|
invalid: [
|
|
207
207
|
{
|
|
208
|
-
name: 'sequence
|
|
208
|
+
name: 'a sequence begin keeps its parentheses',
|
|
209
209
|
code: dedent`
|
|
210
210
|
import { range } from 'ts-data-forge';
|
|
211
211
|
declare const n: number;
|
|
212
|
-
declare const step: number;
|
|
213
212
|
declare const log: () => void;
|
|
214
|
-
for (let i = (log(), 0); i <
|
|
213
|
+
for (let i = (log(), 0); i < n; ++i) {
|
|
215
214
|
console.log(i);
|
|
216
215
|
}
|
|
217
216
|
`,
|
|
218
217
|
output: dedent`
|
|
219
218
|
import { range } from 'ts-data-forge';
|
|
220
219
|
declare const n: number;
|
|
221
|
-
declare const step: number;
|
|
222
220
|
declare const log: () => void;
|
|
223
|
-
for (const i of range((log(), 0),
|
|
221
|
+
for (const i of range((log(), 0), n)) {
|
|
224
222
|
console.log(i);
|
|
225
223
|
}
|
|
226
224
|
`,
|
|
@@ -229,3 +227,127 @@ describe('prefer-range-for-loop with parenthesized operands', () => {
|
|
|
229
227
|
],
|
|
230
228
|
});
|
|
231
229
|
}, 20000);
|
|
230
|
+
|
|
231
|
+
describe('prefer-range-for-loop where the rewrite would change behavior', () => {
|
|
232
|
+
tester.run('prefer-range-for-loop', preferRangeForLoop, {
|
|
233
|
+
valid: [
|
|
234
|
+
{
|
|
235
|
+
// `const i` could not be assigned.
|
|
236
|
+
name: 'the loop variable assigned in the body',
|
|
237
|
+
code: dedent`
|
|
238
|
+
declare const n: number;
|
|
239
|
+
declare const skip: boolean;
|
|
240
|
+
for (let i = 0; i < n; ++i) {
|
|
241
|
+
if (skip) i += 1;
|
|
242
|
+
}
|
|
243
|
+
for (let j = 0; j < n; ++j) {
|
|
244
|
+
j = n;
|
|
245
|
+
}
|
|
246
|
+
`,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
// `range` takes safe integers only.
|
|
250
|
+
name: 'a step or a begin that is not an integer',
|
|
251
|
+
code: dedent`
|
|
252
|
+
declare const n: number;
|
|
253
|
+
for (let i = 0; i < n; i += 0.5) {
|
|
254
|
+
console.log(i);
|
|
255
|
+
}
|
|
256
|
+
for (let j = 0.5; j < n; ++j) {
|
|
257
|
+
console.log(j);
|
|
258
|
+
}
|
|
259
|
+
`,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
// `i < n` with a negative step never ends; `range` yields nothing.
|
|
263
|
+
name: 'a negative literal step',
|
|
264
|
+
code: dedent`
|
|
265
|
+
declare const n: number;
|
|
266
|
+
for (let i = 0; i < n; i += -1) {
|
|
267
|
+
console.log(i);
|
|
268
|
+
}
|
|
269
|
+
`,
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
invalid: [
|
|
273
|
+
{
|
|
274
|
+
// The loop tests its end on every iteration; `range` reads it once.
|
|
275
|
+
name: 'an end that is not a literal or a never-reassigned variable gets no fix',
|
|
276
|
+
code: dedent`
|
|
277
|
+
declare const xs: number[];
|
|
278
|
+
declare const getN: () => number;
|
|
279
|
+
let m = 3;
|
|
280
|
+
for (let i = 0; i < xs.length; ++i) {
|
|
281
|
+
xs.pop();
|
|
282
|
+
}
|
|
283
|
+
for (let j = 0; j < getN(); ++j) {
|
|
284
|
+
console.log(j);
|
|
285
|
+
}
|
|
286
|
+
for (let k = 0; k < m; ++k) {
|
|
287
|
+
m -= 1;
|
|
288
|
+
}
|
|
289
|
+
`,
|
|
290
|
+
output: null,
|
|
291
|
+
errors: [
|
|
292
|
+
{ messageId: 'useRangeForLoop' },
|
|
293
|
+
{ messageId: 'useRangeForLoop' },
|
|
294
|
+
{ messageId: 'useRangeForLoop' },
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
// The loop adds the step it reads on each iteration; `range` reads it
|
|
299
|
+
// once.
|
|
300
|
+
name: 'a variable step that is reassigned gets no fix',
|
|
301
|
+
code: dedent`
|
|
302
|
+
declare const n: number;
|
|
303
|
+
let step = 1;
|
|
304
|
+
for (let i = 0; i < n; i += step) {
|
|
305
|
+
step *= 2;
|
|
306
|
+
}
|
|
307
|
+
`,
|
|
308
|
+
output: null,
|
|
309
|
+
errors: [{ messageId: 'useRangeForLoop' }],
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
// The type checker judges it: a `number` step makes the fixed call
|
|
313
|
+
// fail to type-check.
|
|
314
|
+
name: 'a variable step is fixed without asking its type',
|
|
315
|
+
code: dedent`
|
|
316
|
+
declare const n: number;
|
|
317
|
+
declare const step: number;
|
|
318
|
+
for (let i = 0; i < n; i += step) {
|
|
319
|
+
console.log(i);
|
|
320
|
+
}
|
|
321
|
+
`,
|
|
322
|
+
output: dedent`
|
|
323
|
+
import { range } from 'ts-data-forge';
|
|
324
|
+
declare const n: number;
|
|
325
|
+
declare const step: number;
|
|
326
|
+
for (const i of range(0, n, step)) {
|
|
327
|
+
console.log(i);
|
|
328
|
+
}
|
|
329
|
+
`,
|
|
330
|
+
errors: [{ messageId: 'useRangeForLoop' }],
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: 'a parameter that is never reassigned is a fixed end',
|
|
334
|
+
code: dedent`
|
|
335
|
+
const f = (n: number): void => {
|
|
336
|
+
for (let i = 0; i < n; ++i) {
|
|
337
|
+
console.log(i);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
`,
|
|
341
|
+
output: dedent`
|
|
342
|
+
import { range } from 'ts-data-forge';
|
|
343
|
+
const f = (n: number): void => {
|
|
344
|
+
for (const i of range(0, n)) {
|
|
345
|
+
console.log(i);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
`,
|
|
349
|
+
errors: [{ messageId: 'useRangeForLoop' }],
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
}, 20000);
|
package/src/rules/rules.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ESLintPlugin } from '../types.mjs';
|
|
2
2
|
import { noSideEffectImport } from './no-side-effect-import.mjs';
|
|
3
3
|
import { noUnnecessaryTypeGuard } from './no-unnecessary-type-guard.mjs';
|
|
4
|
+
import { preferArrGenerate } from './prefer-arr-generate.mjs';
|
|
4
5
|
import { preferArrIsArray } from './prefer-arr-is-array.mjs';
|
|
5
6
|
import { preferArrScan } from './prefer-arr-scan.mjs';
|
|
6
7
|
import { preferArrSum } from './prefer-arr-sum.mjs';
|
|
@@ -21,6 +22,7 @@ export const tsDataForgeRules = {
|
|
|
21
22
|
'prefer-canonical-array-slicing': preferCanonicalArraySlicing,
|
|
22
23
|
'prefer-canonical-length-guard': preferCanonicalLengthGuard,
|
|
23
24
|
'prefer-canonical-length-cast': preferCanonicalLengthCast,
|
|
25
|
+
'prefer-arr-generate': preferArrGenerate,
|
|
24
26
|
'prefer-arr-is-array': preferArrIsArray,
|
|
25
27
|
'prefer-arr-scan': preferArrScan,
|
|
26
28
|
'prefer-arr-sum': preferArrSum,
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type TSESLint, type TSESTree } from '@typescript-eslint/utils';
|
|
2
|
-
import type { DeepReadonly } from 'ts-type-forge';
|
|
3
|
-
export declare const getTsDataForgeImport: (program: TSESTree.Program) => TSESTree.ImportDeclaration | undefined;
|
|
4
|
-
export declare const getNamedImports: (node: DeepReadonly<TSESTree.ImportDeclaration> | undefined) => readonly string[];
|
|
5
|
-
/**
|
|
6
|
-
* Returns the in-scope local name a given canonical export is imported as
|
|
7
|
-
* (handling aliases such as `import { isX as y }` → `'y'`), or `undefined` when
|
|
8
|
-
* it is not imported. Used so autofixes reference the binding that actually
|
|
9
|
-
* exists rather than the canonical name.
|
|
10
|
-
*/
|
|
11
|
-
export declare const getImportedLocalName: (node: DeepReadonly<TSESTree.ImportDeclaration> | undefined, importedName: string) => string | undefined;
|
|
12
|
-
type ResolvedCallee = Readonly<{
|
|
13
|
-
/** The canonical (imported) ts-data-forge name, regardless of local alias. */
|
|
14
|
-
canonicalName: string;
|
|
15
|
-
/** The identifier node to rewrite when replacing the callee name. */
|
|
16
|
-
propertyNode: TSESTree.Node;
|
|
17
|
-
/** Whether the call was made through a namespace import (`tf.isX(...)`). */
|
|
18
|
-
isNamespace: boolean;
|
|
19
|
-
}>;
|
|
20
|
-
/**
|
|
21
|
-
* Builds a resolver that maps a call expression's callee to the canonical
|
|
22
|
-
* ts-data-forge function it refers to. Handles named imports (including aliases
|
|
23
|
-
* such as `import { isX as y }`) and namespace imports (`import * as tf` →
|
|
24
|
-
* `tf.isX(...)`). Returns `undefined` for any callee that does not reference the
|
|
25
|
-
* given ts-data-forge import.
|
|
26
|
-
*/
|
|
27
|
-
export declare const buildCalleeResolver: (importDecl: TSESTree.ImportDeclaration | undefined) => ((callee: DeepReadonly<TSESTree.Expression>) => ResolvedCallee | undefined);
|
|
28
|
-
export declare const buildImportFixes: (fixer: TSESLint.RuleFixer, program: TSESTree.Program, _tsDataForgeImport: TSESTree.ImportDeclaration | undefined, requiredNames: readonly string[]) => readonly TSESLint.RuleFix[];
|
|
29
|
-
export {};
|
|
30
|
-
//# sourceMappingURL=import-utils.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"import-utils.d.mts","sourceRoot":"","sources":["../../src/rules/import-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAMlD,eAAO,MAAM,oBAAoB,YACtB,QAAQ,CAAC,OAAO,KACxB,QAAQ,CAAC,iBAAiB,GAAG,SAK7B,CAAC;AAEJ,eAAO,MAAM,eAAe,SACpB,YAAY,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GAAG,SAAS,KACzD,SAAS,MAAM,EAiBjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,SACzB,YAAY,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GAAG,SAAS,gBAC5C,MAAM,KACnB,MAAM,GAAG,SAOG,CAAC;AAEhB,KAAK,cAAc,GAAG,QAAQ,CAAC;IAC7B,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC;IAC5B,4EAA4E;IAC5E,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,eAClB,QAAQ,CAAC,iBAAiB,GAAG,SAAS,KACjD,CAAC,CACF,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,KACtC,cAAc,GAAG,SAAS,CAsE9B,CAAC;AAOF,eAAO,MAAM,gBAAgB,UACpB,QAAQ,CAAC,SAAS,WAChB,QAAQ,CAAC,OAAO,sBACL,QAAQ,CAAC,iBAAiB,GAAG,SAAS,iBAC3C,SAAS,MAAM,EAAE,KAC/B,SAAS,QAAQ,CAAC,OAAO,EAa3B,CAAC"}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES, } from '@typescript-eslint/utils';
|
|
2
|
-
import { isTypeWrapper } from './ast-utils.mjs';
|
|
3
|
-
import { TS_DATA_FORGE_MODULE } from './constants.mjs';
|
|
4
|
-
|
|
5
|
-
export const getTsDataForgeImport = (program) => program.body.find((node) => node.type === AST_NODE_TYPES.ImportDeclaration &&
|
|
6
|
-
node.source.value === TS_DATA_FORGE_MODULE);
|
|
7
|
-
export const getNamedImports = (node) => {
|
|
8
|
-
if (node === undefined) {
|
|
9
|
-
return [];
|
|
10
|
-
}
|
|
11
|
-
return node.specifiers.flatMap((specifier) => specifier.type === AST_NODE_TYPES.ImportSpecifier
|
|
12
|
-
? (() => {
|
|
13
|
-
const importedName = specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
14
|
-
? specifier.imported.name
|
|
15
|
-
: specifier.imported.value;
|
|
16
|
-
return typeof importedName === 'string' ? [importedName] : [];
|
|
17
|
-
})()
|
|
18
|
-
: []);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export const getImportedLocalName = (node, importedName) => node?.specifiers.find((specifier) => specifier.type === AST_NODE_TYPES.ImportSpecifier &&
|
|
27
|
-
(specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
28
|
-
? specifier.imported.name
|
|
29
|
-
: specifier.imported.value) === importedName)?.local.name;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export const buildCalleeResolver = (importDecl) => {
|
|
38
|
-
const specifiers = importDecl?.specifiers ?? [];
|
|
39
|
-
const namespaceName = specifiers.find((specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier)?.local.name;
|
|
40
|
-
const localToCanonical = new Map(specifiers.flatMap((specifier) => {
|
|
41
|
-
if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) {
|
|
42
|
-
return [];
|
|
43
|
-
}
|
|
44
|
-
const importedName = specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
45
|
-
? specifier.imported.name
|
|
46
|
-
: specifier.imported.value;
|
|
47
|
-
return typeof importedName === 'string'
|
|
48
|
-
? [[specifier.local.name, importedName]]
|
|
49
|
-
: [];
|
|
50
|
-
}));
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const resolve = (callee) => {
|
|
55
|
-
if (isTypeWrapper(callee)) {
|
|
56
|
-
return resolve(callee.expression);
|
|
57
|
-
}
|
|
58
|
-
if (callee.type === AST_NODE_TYPES.Identifier) {
|
|
59
|
-
const canonicalName = localToCanonical.get(callee.name);
|
|
60
|
-
return canonicalName === undefined
|
|
61
|
-
? undefined
|
|
62
|
-
: {
|
|
63
|
-
canonicalName,
|
|
64
|
-
|
|
65
|
-
propertyNode: callee,
|
|
66
|
-
isNamespace: false,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
if (namespaceName === undefined ||
|
|
70
|
-
callee.type !== AST_NODE_TYPES.MemberExpression ||
|
|
71
|
-
callee.computed ||
|
|
72
|
-
callee.property.type !== AST_NODE_TYPES.Identifier) {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
const object = skipReadonlyTypeWrappers(callee.object);
|
|
76
|
-
return object.type === AST_NODE_TYPES.Identifier &&
|
|
77
|
-
object.name === namespaceName
|
|
78
|
-
? {
|
|
79
|
-
canonicalName: callee.property.name,
|
|
80
|
-
|
|
81
|
-
propertyNode: callee.property,
|
|
82
|
-
isNamespace: true,
|
|
83
|
-
}
|
|
84
|
-
: undefined;
|
|
85
|
-
};
|
|
86
|
-
return resolve;
|
|
87
|
-
};
|
|
88
|
-
const skipReadonlyTypeWrappers = (node) => isTypeWrapper(node) ? skipReadonlyTypeWrappers(node.expression) : node;
|
|
89
|
-
export const buildImportFixes = (fixer, program, _tsDataForgeImport, requiredNames) => {
|
|
90
|
-
const specifierText = requiredNames.join(', ');
|
|
91
|
-
const importStatement = `import { ${specifierText} } from '${TS_DATA_FORGE_MODULE}';`;
|
|
92
|
-
const newLine = '\n';
|
|
93
|
-
const insertionText = `${importStatement}${newLine}`;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return [fixer.insertTextBefore(program, insertionText)];
|
|
97
|
-
};
|
|
98
|
-
//# sourceMappingURL=import-utils.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"import-utils.mjs","sourceRoot":"","sources":["../../src/rules/import-utils.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,uEAAuE;AAEvE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,OAAyB,EACe,EAAE,CAC1C,OAAO,CAAC,IAAI,CAAC,IAAI,CACf,CAAC,IAAI,EAAsC,EAAE,CAC3C,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,iBAAiB;IAC9C,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,oBAAoB,CAC7C,CAAC;AAEJ,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAA0D,EACvC,EAAE;IACrB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAC3C,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe;QAC/C,CAAC,CAAC,CAAC,GAAG,EAAE;YACJ,MAAM,YAAY,GAChB,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;gBACnD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;gBACzB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC;YAE/B,OAAO,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,CAAC,CAAC,EAAE;QACN,CAAC,CAAC,EAAE,CACP,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,IAA0D,EAC1D,YAAoB,EACA,EAAE,CACtB,IAAI,EAAE,UAAU,CAAC,IAAI,CACnB,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe;IACjD,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;QACpD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;QACzB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,YAAY,CACjD,EAAE,KAAK,CAAC,IAAI,CAAC;AAWhB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,UAAkD,EAGnB,EAAE;IACjC,MAAM,UAAU,GAAG,UAAU,EAAE,UAAU,IAAI,EAAE,CAAC;IAEhD,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CACnC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,wBAAwB,CAC1E,EAAE,KAAK,CAAC,IAAI,CAAC;IAEd,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;QAC/B,IAAI,SAAS,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe,EAAE,CAAC;YACtD,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,YAAY,GAChB,SAAS,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;YACnD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;YACzB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC;QAE/B,OAAO,OAAO,YAAY,KAAK,QAAQ;YACrC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAU,CAAC;YACjD,CAAC,CAAC,EAAE,CAAC;IACT,CAAC,CAAC,CACH,CAAC;IAEF,6EAA6E;IAC7E,uEAAuE;IACvE,yBAAyB;IACzB,MAAM,OAAO,GAAG,CACd,MAAyC,EACb,EAAE;QAC9B,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,EAAE,CAAC;YAC9C,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAExD,OAAO,aAAa,KAAK,SAAS;gBAChC,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC;oBACE,aAAa;oBACb,oEAAoE;oBACpE,YAAY,EAAE,MAAuB;oBACrC,WAAW,EAAE,KAAK;iBACnB,CAAC;QACR,CAAC;QAED,IACE,aAAa,KAAK,SAAS;YAC3B,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB;YAC/C,MAAM,CAAC,QAAQ;YACf,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,EAClD,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,MAAM,GAAG,wBAAwB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEvD,OAAO,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;YAC9C,MAAM,CAAC,IAAI,KAAK,aAAa;YAC7B,CAAC,CAAC;gBACE,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI;gBACnC,oEAAoE;gBACpE,YAAY,EAAE,MAAM,CAAC,QAAyB;gBAC9C,WAAW,EAAE,IAAI;aAClB;YACH,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,wBAAwB,GAAG,CAC/B,IAAuC,EACJ,EAAE,CACrC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAEzE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,KAAyB,EACzB,OAAyB,EACzB,kBAA0D,EAC1D,aAAgC,EACH,EAAE;IAC/B,MAAM,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/C,MAAM,eAAe,GACnB,YAAY,aAAa,YAAY,oBAAoB,IAAa,CAAC;IAEzE,MAAM,OAAO,GAAG,IAAI,CAAC;IAErB,MAAM,aAAa,GAAG,GAAG,eAAe,GAAG,OAAO,EAAW,CAAC;IAE9D,6CAA6C;IAC7C,uDAAuD;IACvD,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;AAC1D,CAAC,CAAC"}
|