eslint-plugin-svelte 2.7.0 → 2.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 +4 -2
- package/lib/rules/@typescript-eslint/no-unnecessary-condition.d.ts +2 -0
- package/lib/rules/@typescript-eslint/no-unnecessary-condition.js +448 -0
- package/lib/rules/derived-has-same-inputs-outputs.d.ts +2 -0
- package/lib/rules/derived-has-same-inputs-outputs.js +80 -0
- package/lib/rules/no-store-async.js +1 -1
- package/lib/rules/reference-helpers/svelte-store.d.ts +3 -1
- package/lib/rules/reference-helpers/svelte-store.js +4 -4
- package/lib/rules/system.js +1 -1
- package/lib/types.d.ts +8 -2
- package/lib/utils/rules.js +4 -0
- package/lib/utils/ts-utils/index.d.ts +35 -0
- package/lib/utils/ts-utils/index.js +205 -0
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ This plugin provides configs:
|
|
|
89
89
|
|
|
90
90
|
- `plugin:svelte/base` ... Configuration to enable correct Svelte parsing.
|
|
91
91
|
- `plugin:svelte/recommended` ... Above, plus rules to prevent errors or unintended behavior.
|
|
92
|
-
- `plugin:svelte/prettier` ...
|
|
92
|
+
- `plugin:svelte/prettier` ... Turns off rules that may conflict with [Prettier](https://prettier.io/) (You still need to configure prettier to work with svelte yourself, for example by using [prettier-plugin-svelte](https://github.com/sveltejs/prettier-plugin-svelte).).
|
|
93
93
|
|
|
94
94
|
See [the rule list](https://ota-meshi.github.io/eslint-plugin-svelte/rules/) to get the `rules` that this plugin provides.
|
|
95
95
|
|
|
@@ -303,6 +303,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
|
|
|
303
303
|
|
|
304
304
|
| Rule ID | Description | |
|
|
305
305
|
|:--------|:------------|:---|
|
|
306
|
+
| [svelte/derived-has-same-inputs-outputs](https://ota-meshi.github.io/eslint-plugin-svelte/rules/derived-has-same-inputs-outputs/) | derived store should use same variable names between values and callback | |
|
|
306
307
|
| [svelte/first-attribute-linebreak](https://ota-meshi.github.io/eslint-plugin-svelte/rules/first-attribute-linebreak/) | enforce the location of first attribute | :wrench: |
|
|
307
308
|
| [svelte/html-closing-bracket-spacing](https://ota-meshi.github.io/eslint-plugin-svelte/rules/html-closing-bracket-spacing/) | require or disallow a space before tag's closing brackets | :wrench: |
|
|
308
309
|
| [svelte/html-quotes](https://ota-meshi.github.io/eslint-plugin-svelte/rules/html-quotes/) | enforce quotes style of HTML attributes | :wrench: |
|
|
@@ -321,10 +322,11 @@ These rules relate to style guidelines, and are therefore quite subjective:
|
|
|
321
322
|
|
|
322
323
|
## Extension Rules
|
|
323
324
|
|
|
324
|
-
These rules extend the rules provided by ESLint itself to work well in Svelte:
|
|
325
|
+
These rules extend the rules provided by ESLint itself, or other plugins to work well in Svelte:
|
|
325
326
|
|
|
326
327
|
| Rule ID | Description | |
|
|
327
328
|
|:--------|:------------|:---|
|
|
329
|
+
| [svelte/@typescript-eslint/no-unnecessary-condition](https://ota-meshi.github.io/eslint-plugin-svelte/rules/@typescript-eslint/no-unnecessary-condition/) | disallow conditionals where the type is always truthy or always falsy | :wrench: |
|
|
328
330
|
| [svelte/no-inner-declarations](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-inner-declarations/) | disallow variable or `function` declarations in nested blocks | :star: |
|
|
329
331
|
| [svelte/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-trailing-spaces/) | disallow trailing whitespace at the end of lines | :wrench: |
|
|
330
332
|
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("../../utils");
|
|
4
|
+
const ts_utils_1 = require("../../utils/ts-utils");
|
|
5
|
+
function unionTypeParts(type) {
|
|
6
|
+
return [...iterate(type)];
|
|
7
|
+
function* iterate(t) {
|
|
8
|
+
if (t.isUnion()) {
|
|
9
|
+
for (const type of t.types) {
|
|
10
|
+
yield* iterate(type);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
yield t;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function isPossiblyFalsy(type, tsTools) {
|
|
19
|
+
return (unionTypeParts(type)
|
|
20
|
+
.filter((t) => !(0, ts_utils_1.isTruthyLiteral)(t, tsTools))
|
|
21
|
+
.some((type) => (0, ts_utils_1.isPossiblyFalsyType)(type, tsTools.ts)));
|
|
22
|
+
}
|
|
23
|
+
function isPossiblyTruthy(type, tsTools) {
|
|
24
|
+
return unionTypeParts(type).some((type) => !(0, ts_utils_1.isFalsyType)(type, tsTools));
|
|
25
|
+
}
|
|
26
|
+
function isPossiblyNullish(type, tsTools) {
|
|
27
|
+
return (0, ts_utils_1.isNullableType)(type, tsTools.ts);
|
|
28
|
+
}
|
|
29
|
+
function isAlwaysNullish(type, tsTools) {
|
|
30
|
+
return (0, ts_utils_1.isNullishType)(type, tsTools.ts);
|
|
31
|
+
}
|
|
32
|
+
function isLiteral(type, tsTools) {
|
|
33
|
+
return ((0, ts_utils_1.isBooleanLiteralType)(type, tsTools.ts) ||
|
|
34
|
+
(0, ts_utils_1.isNullishType)(type, tsTools.ts) ||
|
|
35
|
+
type.isLiteral());
|
|
36
|
+
}
|
|
37
|
+
exports.default = (0, utils_1.createRule)("@typescript-eslint/no-unnecessary-condition", {
|
|
38
|
+
meta: {
|
|
39
|
+
docs: {
|
|
40
|
+
description: "disallow conditionals where the type is always truthy or always falsy",
|
|
41
|
+
category: "Extension Rules",
|
|
42
|
+
recommended: false,
|
|
43
|
+
extensionRule: {
|
|
44
|
+
plugin: "@typescript-eslint/eslint-plugin",
|
|
45
|
+
url: "https://typescript-eslint.io/rules/no-unnecessary-condition/",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
schema: [
|
|
49
|
+
{
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
allowConstantLoopConditions: {
|
|
53
|
+
description: "Whether to ignore constant loop conditions, such as `while (true)`.",
|
|
54
|
+
type: "boolean",
|
|
55
|
+
},
|
|
56
|
+
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: {
|
|
57
|
+
description: "Whether to not error when running with a tsconfig that has strictNullChecks turned.",
|
|
58
|
+
type: "boolean",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
fixable: "code",
|
|
65
|
+
messages: {
|
|
66
|
+
alwaysTruthy: "Unnecessary conditional, value is always truthy.",
|
|
67
|
+
alwaysFalsy: "Unnecessary conditional, value is always falsy.",
|
|
68
|
+
alwaysTruthyFunc: "This callback should return a conditional, but return is always truthy.",
|
|
69
|
+
alwaysFalsyFunc: "This callback should return a conditional, but return is always falsy.",
|
|
70
|
+
neverNullish: "Unnecessary conditional, expected left-hand side of `??` operator to be possibly null or undefined.",
|
|
71
|
+
alwaysNullish: "Unnecessary conditional, left-hand side of `??` operator is always `null` or `undefined`.",
|
|
72
|
+
literalBooleanExpression: "Unnecessary conditional, both sides of the expression are literal values.",
|
|
73
|
+
noOverlapBooleanExpression: "Unnecessary conditional, the types have no overlap.",
|
|
74
|
+
never: "Unnecessary conditional, value is `never`.",
|
|
75
|
+
neverOptionalChain: "Unnecessary optional chain on a non-nullish value.",
|
|
76
|
+
noStrictNullCheck: "This rule requires the `strictNullChecks` compiler option to be turned on to function correctly.",
|
|
77
|
+
},
|
|
78
|
+
type: "suggestion",
|
|
79
|
+
},
|
|
80
|
+
create(context) {
|
|
81
|
+
const { allowConstantLoopConditions = false, allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing = false, } = (context.options[0] || {});
|
|
82
|
+
const tools = (0, ts_utils_1.getTypeScriptTools)(context);
|
|
83
|
+
if (!tools) {
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
const { service, ts } = tools;
|
|
87
|
+
const checker = service.program.getTypeChecker();
|
|
88
|
+
const sourceCode = context.getSourceCode();
|
|
89
|
+
const compilerOptions = service.program.getCompilerOptions();
|
|
90
|
+
const isStrictNullChecks = compilerOptions.strict
|
|
91
|
+
? compilerOptions.strictNullChecks !== false
|
|
92
|
+
: compilerOptions.strictNullChecks;
|
|
93
|
+
if (!isStrictNullChecks &&
|
|
94
|
+
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing !== true) {
|
|
95
|
+
context.report({
|
|
96
|
+
loc: {
|
|
97
|
+
start: { line: 0, column: 0 },
|
|
98
|
+
end: { line: 0, column: 0 },
|
|
99
|
+
},
|
|
100
|
+
messageId: "noStrictNullCheck",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const mutableVarReferenceIds = [];
|
|
104
|
+
const scriptElements = [];
|
|
105
|
+
let inSvelteReactiveStatement = false;
|
|
106
|
+
for (const scope of [
|
|
107
|
+
sourceCode.scopeManager.globalScope,
|
|
108
|
+
sourceCode.scopeManager.globalScope?.childScopes.find((scope) => scope.type === "module"),
|
|
109
|
+
]) {
|
|
110
|
+
if (!scope)
|
|
111
|
+
continue;
|
|
112
|
+
for (const variable of scope.variables) {
|
|
113
|
+
if (variable.defs.some((def) => def.type === "Variable" &&
|
|
114
|
+
(def.parent.kind === "var" || def.parent.kind === "let"))) {
|
|
115
|
+
for (const reference of variable.references) {
|
|
116
|
+
mutableVarReferenceIds.push(reference.identifier);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const body of sourceCode.ast.body) {
|
|
122
|
+
if (body.type === "SvelteScriptElement") {
|
|
123
|
+
scriptElements.push(body);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function hasSvelteReactiveVar(node) {
|
|
127
|
+
const inReactiveScope = inSvelteReactiveStatement ||
|
|
128
|
+
(scriptElements.length &&
|
|
129
|
+
scriptElements.every((elem) => node.range[1] <= elem.range[0] || elem.range[1] <= node.range[0]));
|
|
130
|
+
if (!inReactiveScope) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
return mutableVarReferenceIds.some((id) => node.range[0] <= id.range[0] && id.range[1] <= node.range[1]);
|
|
134
|
+
}
|
|
135
|
+
function getNodeType(node) {
|
|
136
|
+
const tsNode = service.esTreeNodeToTSNodeMap.get(node);
|
|
137
|
+
return tsNode && (0, ts_utils_1.getConstrainedTypeAtLocation)(checker, tsNode);
|
|
138
|
+
}
|
|
139
|
+
function nodeIsArrayType(node) {
|
|
140
|
+
const nodeType = getNodeType(node);
|
|
141
|
+
if (!nodeType) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return checker.isArrayType(nodeType);
|
|
145
|
+
}
|
|
146
|
+
function nodeIsTupleType(node) {
|
|
147
|
+
const nodeType = getNodeType(node);
|
|
148
|
+
return Boolean(nodeType && (0, ts_utils_1.isTupleType)(nodeType, ts));
|
|
149
|
+
}
|
|
150
|
+
function isArrayIndexExpression(node) {
|
|
151
|
+
return (node.type === "MemberExpression" &&
|
|
152
|
+
node.computed &&
|
|
153
|
+
(nodeIsArrayType(node.object) ||
|
|
154
|
+
(nodeIsTupleType(node.object) &&
|
|
155
|
+
node.property.type !== "Literal")));
|
|
156
|
+
}
|
|
157
|
+
function checkNode(node, isUnaryNotArgument = false) {
|
|
158
|
+
if (hasSvelteReactiveVar(node)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (node.type === "UnaryExpression" && node.operator === "!") {
|
|
162
|
+
checkNode(node.argument, true);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (isArrayIndexExpression(node)) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (node.type === "LogicalExpression" && node.operator !== "??") {
|
|
169
|
+
checkNode(node.right);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const type = getNodeType(node);
|
|
173
|
+
if (!type ||
|
|
174
|
+
unionTypeParts(type).some((part) => (0, ts_utils_1.isAnyType)(part, ts) ||
|
|
175
|
+
(0, ts_utils_1.isUnknownType)(part, ts) ||
|
|
176
|
+
part.isTypeParameter())) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
let messageId = null;
|
|
180
|
+
if (unionTypeParts(type).some((part) => (0, ts_utils_1.isNeverType)(part, ts))) {
|
|
181
|
+
messageId = "never";
|
|
182
|
+
}
|
|
183
|
+
else if (!isPossiblyTruthy(type, tools)) {
|
|
184
|
+
messageId = !isUnaryNotArgument ? "alwaysFalsy" : "alwaysTruthy";
|
|
185
|
+
}
|
|
186
|
+
else if (!isPossiblyFalsy(type, tools)) {
|
|
187
|
+
messageId = !isUnaryNotArgument ? "alwaysTruthy" : "alwaysFalsy";
|
|
188
|
+
}
|
|
189
|
+
if (messageId) {
|
|
190
|
+
context.report({ node, messageId });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function checkNodeForNullish(node) {
|
|
194
|
+
if (hasSvelteReactiveVar(node)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const type = getNodeType(node);
|
|
198
|
+
if (!type || (0, ts_utils_1.isAnyType)(type, ts) || (0, ts_utils_1.isUnknownType)(type, ts)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
let messageId = null;
|
|
202
|
+
if (unionTypeParts(type).some((part) => (0, ts_utils_1.isNeverType)(part, ts))) {
|
|
203
|
+
messageId = "never";
|
|
204
|
+
}
|
|
205
|
+
else if (!isPossiblyNullish(type, tools)) {
|
|
206
|
+
if (!isArrayIndexExpression(node) &&
|
|
207
|
+
!(node.type === "ChainExpression" &&
|
|
208
|
+
node.expression.type !== "TSNonNullExpression" &&
|
|
209
|
+
optionChainContainsOptionArrayIndex(node.expression))) {
|
|
210
|
+
messageId = "neverNullish";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (isAlwaysNullish(type, tools)) {
|
|
214
|
+
messageId = "alwaysNullish";
|
|
215
|
+
}
|
|
216
|
+
if (messageId) {
|
|
217
|
+
context.report({ node, messageId });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const BOOL_OPERATORS = new Set([
|
|
221
|
+
"<",
|
|
222
|
+
">",
|
|
223
|
+
"<=",
|
|
224
|
+
">=",
|
|
225
|
+
"==",
|
|
226
|
+
"===",
|
|
227
|
+
"!=",
|
|
228
|
+
"!==",
|
|
229
|
+
]);
|
|
230
|
+
function checkIfBinaryExpressionIsNecessaryConditional(node) {
|
|
231
|
+
if (hasSvelteReactiveVar(node)) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!BOOL_OPERATORS.has(node.operator)) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const leftType = getNodeType(node.left);
|
|
238
|
+
const rightType = getNodeType(node.right);
|
|
239
|
+
if (!leftType || !rightType) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (isLiteral(leftType, tools) && isLiteral(rightType, tools)) {
|
|
243
|
+
context.report({ node, messageId: "literalBooleanExpression" });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (isStrictNullChecks) {
|
|
247
|
+
const UNDEFINED = ts.TypeFlags.Undefined;
|
|
248
|
+
const NULL = ts.TypeFlags.Null;
|
|
249
|
+
const isComparable = (type, f) => {
|
|
250
|
+
let flag = f;
|
|
251
|
+
flag |=
|
|
252
|
+
ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.TypeParameter;
|
|
253
|
+
if (node.operator === "==" || node.operator === "!=") {
|
|
254
|
+
flag |= NULL | UNDEFINED;
|
|
255
|
+
}
|
|
256
|
+
return unionTypeParts(type).some((t) => (t.flags & flag) !== 0);
|
|
257
|
+
};
|
|
258
|
+
if ((leftType.flags === UNDEFINED &&
|
|
259
|
+
!isComparable(rightType, UNDEFINED)) ||
|
|
260
|
+
(rightType.flags === UNDEFINED &&
|
|
261
|
+
!isComparable(leftType, UNDEFINED)) ||
|
|
262
|
+
(leftType.flags === NULL && !isComparable(rightType, NULL)) ||
|
|
263
|
+
(rightType.flags === NULL && !isComparable(leftType, NULL))) {
|
|
264
|
+
context.report({ node, messageId: "noOverlapBooleanExpression" });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function checkLogicalExpressionForUnnecessaryConditionals(node) {
|
|
269
|
+
if (node.operator === "??") {
|
|
270
|
+
checkNodeForNullish(node.left);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
checkNode(node.left);
|
|
274
|
+
}
|
|
275
|
+
function checkIfLoopIsNecessaryConditional(node) {
|
|
276
|
+
if (node.test === null) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (allowConstantLoopConditions) {
|
|
280
|
+
const nodeType = getNodeType(node.test);
|
|
281
|
+
if (nodeType &&
|
|
282
|
+
(0, ts_utils_1.isBooleanLiteralType)(nodeType, ts) &&
|
|
283
|
+
checker.typeToString(nodeType) === "true")
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
checkNode(node.test);
|
|
287
|
+
}
|
|
288
|
+
const ARRAY_PREDICATE_FUNCTIONS = new Set([
|
|
289
|
+
"filter",
|
|
290
|
+
"find",
|
|
291
|
+
"some",
|
|
292
|
+
"every",
|
|
293
|
+
]);
|
|
294
|
+
function isArrayPredicateFunction(node) {
|
|
295
|
+
const { callee } = node;
|
|
296
|
+
return (callee.type === "MemberExpression" &&
|
|
297
|
+
callee.property.type === "Identifier" &&
|
|
298
|
+
ARRAY_PREDICATE_FUNCTIONS.has(callee.property.name) &&
|
|
299
|
+
(nodeIsArrayType(callee.object) || nodeIsTupleType(callee.object)));
|
|
300
|
+
}
|
|
301
|
+
function checkCallExpression(node) {
|
|
302
|
+
if (isArrayPredicateFunction(node) && node.arguments.length) {
|
|
303
|
+
const callback = node.arguments[0];
|
|
304
|
+
if ((callback.type === "ArrowFunctionExpression" ||
|
|
305
|
+
callback.type === "FunctionExpression") &&
|
|
306
|
+
callback.body) {
|
|
307
|
+
if (callback.body.type !== "BlockStatement") {
|
|
308
|
+
checkNode(callback.body);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const callbackBody = callback.body.body;
|
|
312
|
+
if (callbackBody.length === 1 &&
|
|
313
|
+
callbackBody[0].type === "ReturnStatement" &&
|
|
314
|
+
callbackBody[0].argument) {
|
|
315
|
+
checkNode(callbackBody[0].argument);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const nodeType = getNodeType(callback);
|
|
320
|
+
if (!nodeType) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const returnTypes = (0, ts_utils_1.getCallSignaturesOfType)(nodeType).map((sig) => sig.getReturnType());
|
|
324
|
+
if (returnTypes.length === 0) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (returnTypes.some((t) => (0, ts_utils_1.isAnyType)(t, ts) || (0, ts_utils_1.isUnknownType)(t, ts))) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (!returnTypes.some((t) => isPossiblyFalsy(t, tools))) {
|
|
331
|
+
context.report({
|
|
332
|
+
node: callback,
|
|
333
|
+
messageId: "alwaysTruthyFunc",
|
|
334
|
+
});
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (!returnTypes.some((t) => isPossiblyTruthy(t, tools))) {
|
|
338
|
+
context.report({
|
|
339
|
+
node: callback,
|
|
340
|
+
messageId: "alwaysFalsyFunc",
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function optionChainContainsOptionArrayIndex(node) {
|
|
346
|
+
const lhsNode = node.type === "CallExpression" ? node.callee : node.object;
|
|
347
|
+
if (node.optional && isArrayIndexExpression(lhsNode)) {
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
if (lhsNode.type === "MemberExpression" ||
|
|
351
|
+
lhsNode.type === "CallExpression") {
|
|
352
|
+
return optionChainContainsOptionArrayIndex(lhsNode);
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
function isNullablePropertyType(objType, propertyType) {
|
|
357
|
+
if (propertyType.isUnion()) {
|
|
358
|
+
return propertyType.types.some((type) => isNullablePropertyType(objType, type));
|
|
359
|
+
}
|
|
360
|
+
if (propertyType.isNumberLiteral() || propertyType.isStringLiteral()) {
|
|
361
|
+
const propType = (0, ts_utils_1.getTypeOfPropertyOfType)(objType, propertyType.value.toString(), checker);
|
|
362
|
+
if (propType) {
|
|
363
|
+
return (0, ts_utils_1.isNullableType)(propType, ts);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const typeName = (0, ts_utils_1.getTypeName)(propertyType, tools);
|
|
367
|
+
return Boolean((typeName === "string" &&
|
|
368
|
+
checker.getIndexInfoOfType(objType, ts.IndexKind.String)) ||
|
|
369
|
+
(typeName === "number" &&
|
|
370
|
+
checker.getIndexInfoOfType(objType, ts.IndexKind.Number)));
|
|
371
|
+
}
|
|
372
|
+
function isNullableOriginFromPrev(node) {
|
|
373
|
+
const prevType = getNodeType(node.object);
|
|
374
|
+
const property = node.property;
|
|
375
|
+
if (prevType && prevType.isUnion() && property.type === "Identifier") {
|
|
376
|
+
const isOwnNullable = prevType.types.some((type) => {
|
|
377
|
+
if (node.computed) {
|
|
378
|
+
const propertyType = getNodeType(node.property);
|
|
379
|
+
return Boolean(propertyType && isNullablePropertyType(type, propertyType));
|
|
380
|
+
}
|
|
381
|
+
const propType = (0, ts_utils_1.getTypeOfPropertyOfType)(type, property.name, checker);
|
|
382
|
+
return propType && (0, ts_utils_1.isNullableType)(propType, ts);
|
|
383
|
+
});
|
|
384
|
+
return !isOwnNullable && (0, ts_utils_1.isNullableType)(prevType, ts);
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
function isOptionableExpression(node) {
|
|
389
|
+
const type = getNodeType(node);
|
|
390
|
+
if (!type) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
const isOwnNullable = node.type === "MemberExpression"
|
|
394
|
+
? !isNullableOriginFromPrev(node)
|
|
395
|
+
: true;
|
|
396
|
+
return ((0, ts_utils_1.isAnyType)(type, ts) ||
|
|
397
|
+
(0, ts_utils_1.isUnknownType)(type, ts) ||
|
|
398
|
+
((0, ts_utils_1.isNullableType)(type, ts) && isOwnNullable));
|
|
399
|
+
}
|
|
400
|
+
function checkOptionalChain(node, beforeOperator, fix) {
|
|
401
|
+
if (!node.optional) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (optionChainContainsOptionArrayIndex(node)) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const nodeToCheck = node.type === "CallExpression" ? node.callee : node.object;
|
|
408
|
+
if (hasSvelteReactiveVar(nodeToCheck)) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (isOptionableExpression(nodeToCheck)) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const questionDotOperator = sourceCode.getTokenAfter(beforeOperator, {
|
|
415
|
+
includeComments: false,
|
|
416
|
+
filter: (token) => token.type === "Punctuator" && token.value === "?.",
|
|
417
|
+
});
|
|
418
|
+
context.report({
|
|
419
|
+
node,
|
|
420
|
+
loc: questionDotOperator.loc,
|
|
421
|
+
messageId: "neverOptionalChain",
|
|
422
|
+
fix(fixer) {
|
|
423
|
+
return fixer.replaceText(questionDotOperator, fix);
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
function checkOptionalMemberExpression(node) {
|
|
428
|
+
checkOptionalChain(node, node.object, node.computed ? "" : ".");
|
|
429
|
+
}
|
|
430
|
+
function checkOptionalCallExpression(node) {
|
|
431
|
+
checkOptionalChain(node, node.callee, "");
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
SvelteReactiveStatement: () => (inSvelteReactiveStatement = true),
|
|
435
|
+
"SvelteReactiveStatement:exit": () => (inSvelteReactiveStatement = false),
|
|
436
|
+
BinaryExpression: checkIfBinaryExpressionIsNecessaryConditional,
|
|
437
|
+
CallExpression: checkCallExpression,
|
|
438
|
+
ConditionalExpression: (node) => checkNode(node.test),
|
|
439
|
+
DoWhileStatement: checkIfLoopIsNecessaryConditional,
|
|
440
|
+
ForStatement: checkIfLoopIsNecessaryConditional,
|
|
441
|
+
IfStatement: (node) => checkNode(node.test),
|
|
442
|
+
LogicalExpression: checkLogicalExpressionForUnnecessaryConditionals,
|
|
443
|
+
WhileStatement: checkIfLoopIsNecessaryConditional,
|
|
444
|
+
"MemberExpression[optional = true]": checkOptionalMemberExpression,
|
|
445
|
+
"CallExpression[optional = true]": checkOptionalCallExpression,
|
|
446
|
+
};
|
|
447
|
+
},
|
|
448
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("../utils");
|
|
4
|
+
const svelte_store_1 = require("./reference-helpers/svelte-store");
|
|
5
|
+
exports.default = (0, utils_1.createRule)("derived-has-same-inputs-outputs", {
|
|
6
|
+
meta: {
|
|
7
|
+
docs: {
|
|
8
|
+
description: "derived store should use same variable names between values and callback",
|
|
9
|
+
category: "Stylistic Issues",
|
|
10
|
+
recommended: false,
|
|
11
|
+
conflictWithPrettier: false,
|
|
12
|
+
},
|
|
13
|
+
schema: [],
|
|
14
|
+
messages: {
|
|
15
|
+
unexpected: "The argument name should be '{{name}}'.",
|
|
16
|
+
},
|
|
17
|
+
type: "suggestion",
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
function isIdentifierOrArrayExpression(node) {
|
|
21
|
+
return ["Identifier", "ArrayExpression"].includes(node.type);
|
|
22
|
+
}
|
|
23
|
+
function isFunctionExpression(node) {
|
|
24
|
+
return ["ArrowFunctionExpression", "FunctionExpression"].includes(node.type);
|
|
25
|
+
}
|
|
26
|
+
function checkIdentifier(context, args, fn) {
|
|
27
|
+
const fnParam = fn.params[0];
|
|
28
|
+
if (fnParam.type !== "Identifier")
|
|
29
|
+
return;
|
|
30
|
+
const expectedName = `$${args.name}`;
|
|
31
|
+
if (expectedName !== fnParam.name) {
|
|
32
|
+
context.report({
|
|
33
|
+
node: fn,
|
|
34
|
+
loc: fnParam.loc,
|
|
35
|
+
messageId: "unexpected",
|
|
36
|
+
data: { name: expectedName },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function checkArrayExpression(context, args, fn) {
|
|
41
|
+
const fnParam = fn.params[0];
|
|
42
|
+
if (fnParam.type !== "ArrayPattern")
|
|
43
|
+
return;
|
|
44
|
+
const argNames = args.elements.map((element) => {
|
|
45
|
+
return element && element.type === "Identifier" ? element.name : null;
|
|
46
|
+
});
|
|
47
|
+
fnParam.elements.forEach((element, index) => {
|
|
48
|
+
const argName = argNames[index];
|
|
49
|
+
if (element && element.type === "Identifier" && argName) {
|
|
50
|
+
const expectedName = `$${argName}`;
|
|
51
|
+
if (expectedName !== element.name) {
|
|
52
|
+
context.report({
|
|
53
|
+
node: fn,
|
|
54
|
+
loc: element.loc,
|
|
55
|
+
messageId: "unexpected",
|
|
56
|
+
data: { name: expectedName },
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
Program() {
|
|
64
|
+
for (const { node } of (0, svelte_store_1.extractStoreReferences)(context, ["derived"])) {
|
|
65
|
+
const [args, fn] = node.arguments;
|
|
66
|
+
if (!args || !isIdentifierOrArrayExpression(args))
|
|
67
|
+
continue;
|
|
68
|
+
if (!fn || !isFunctionExpression(fn))
|
|
69
|
+
continue;
|
|
70
|
+
if (!fn.params || fn.params.length === 0)
|
|
71
|
+
continue;
|
|
72
|
+
if (args.type === "Identifier")
|
|
73
|
+
checkIdentifier(context, args, fn);
|
|
74
|
+
else
|
|
75
|
+
checkArrayExpression(context, args, fn);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type * as ESTree from "estree";
|
|
2
2
|
import type { RuleContext } from "../../types";
|
|
3
|
-
|
|
3
|
+
declare type StoreName = "writable" | "readable" | "derived";
|
|
4
|
+
export declare function extractStoreReferences(context: RuleContext, storeNames?: StoreName[]): Generator<{
|
|
4
5
|
node: ESTree.CallExpression;
|
|
5
6
|
name: string;
|
|
6
7
|
}, void>;
|
|
8
|
+
export {};
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extractStoreReferences = void 0;
|
|
4
4
|
const eslint_utils_1 = require("eslint-utils");
|
|
5
|
-
function* extractStoreReferences(context) {
|
|
5
|
+
function* extractStoreReferences(context, storeNames = ["writable", "readable", "derived"]) {
|
|
6
6
|
const referenceTracker = new eslint_utils_1.ReferenceTracker(context.getScope());
|
|
7
7
|
for (const { node, path } of referenceTracker.iterateEsmReferences({
|
|
8
8
|
"svelte/store": {
|
|
9
9
|
[eslint_utils_1.ReferenceTracker.ESM]: true,
|
|
10
10
|
writable: {
|
|
11
|
-
[eslint_utils_1.ReferenceTracker.CALL]:
|
|
11
|
+
[eslint_utils_1.ReferenceTracker.CALL]: storeNames.includes("writable"),
|
|
12
12
|
},
|
|
13
13
|
readable: {
|
|
14
|
-
[eslint_utils_1.ReferenceTracker.CALL]:
|
|
14
|
+
[eslint_utils_1.ReferenceTracker.CALL]: storeNames.includes("readable"),
|
|
15
15
|
},
|
|
16
16
|
derived: {
|
|
17
|
-
[eslint_utils_1.ReferenceTracker.CALL]:
|
|
17
|
+
[eslint_utils_1.ReferenceTracker.CALL]: storeNames.includes("derived"),
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
})) {
|
package/lib/rules/system.js
CHANGED
|
@@ -60,7 +60,7 @@ exports.default = (0, utils_1.createRule)("system", {
|
|
|
60
60
|
loc: node.startTag.loc.end,
|
|
61
61
|
});
|
|
62
62
|
if (node.endTag) {
|
|
63
|
-
directives.
|
|
63
|
+
directives.disableBlock(node.endTag.loc.start, isIgnoreRule, {
|
|
64
64
|
loc: node.endTag.loc.start,
|
|
65
65
|
});
|
|
66
66
|
}
|
package/lib/types.d.ts
CHANGED
|
@@ -22,7 +22,10 @@ export interface RuleMetaData {
|
|
|
22
22
|
description: string;
|
|
23
23
|
category: RuleCategory;
|
|
24
24
|
recommended: boolean | "base";
|
|
25
|
-
extensionRule?: string
|
|
25
|
+
extensionRule?: string | {
|
|
26
|
+
plugin: string;
|
|
27
|
+
url: string;
|
|
28
|
+
};
|
|
26
29
|
url: string;
|
|
27
30
|
ruleId: string;
|
|
28
31
|
ruleName: string;
|
|
@@ -47,7 +50,10 @@ export interface PartialRuleMetaData {
|
|
|
47
50
|
docs: {
|
|
48
51
|
description: string;
|
|
49
52
|
recommended: boolean | "base";
|
|
50
|
-
extensionRule?: string
|
|
53
|
+
extensionRule?: string | {
|
|
54
|
+
plugin: string;
|
|
55
|
+
url: string;
|
|
56
|
+
};
|
|
51
57
|
default?: "error" | "warn";
|
|
52
58
|
} & ({
|
|
53
59
|
category: Exclude<RuleCategory, "Stylistic Issues">;
|
package/lib/utils/rules.js
CHANGED
|
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.rules = void 0;
|
|
7
|
+
const no_unnecessary_condition_1 = __importDefault(require("../rules/@typescript-eslint/no-unnecessary-condition"));
|
|
7
8
|
const button_has_type_1 = __importDefault(require("../rules/button-has-type"));
|
|
8
9
|
const comment_directive_1 = __importDefault(require("../rules/comment-directive"));
|
|
10
|
+
const derived_has_same_inputs_outputs_1 = __importDefault(require("../rules/derived-has-same-inputs-outputs"));
|
|
9
11
|
const first_attribute_linebreak_1 = __importDefault(require("../rules/first-attribute-linebreak"));
|
|
10
12
|
const html_closing_bracket_spacing_1 = __importDefault(require("../rules/html-closing-bracket-spacing"));
|
|
11
13
|
const html_quotes_1 = __importDefault(require("../rules/html-quotes"));
|
|
@@ -43,8 +45,10 @@ const spaced_html_comment_1 = __importDefault(require("../rules/spaced-html-comm
|
|
|
43
45
|
const system_1 = __importDefault(require("../rules/system"));
|
|
44
46
|
const valid_compile_1 = __importDefault(require("../rules/valid-compile"));
|
|
45
47
|
exports.rules = [
|
|
48
|
+
no_unnecessary_condition_1.default,
|
|
46
49
|
button_has_type_1.default,
|
|
47
50
|
comment_directive_1.default,
|
|
51
|
+
derived_has_same_inputs_outputs_1.default,
|
|
48
52
|
first_attribute_linebreak_1.default,
|
|
49
53
|
html_closing_bracket_spacing_1.default,
|
|
50
54
|
html_quotes_1.default,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RuleContext, ASTNode } from "../../types";
|
|
2
|
+
import type * as TS from "typescript";
|
|
3
|
+
export declare type TypeScript = typeof TS;
|
|
4
|
+
export type { TS };
|
|
5
|
+
export declare type TSTools = {
|
|
6
|
+
service: {
|
|
7
|
+
esTreeNodeToTSNodeMap: ReadonlyMap<unknown, TS.Node>;
|
|
8
|
+
tsNodeToESTreeNodeMap: ReadonlyMap<TS.Node, ASTNode>;
|
|
9
|
+
program: TS.Program;
|
|
10
|
+
hasFullTypeInformation: boolean;
|
|
11
|
+
};
|
|
12
|
+
ts: TypeScript;
|
|
13
|
+
};
|
|
14
|
+
export declare function getTypeScriptTools(context: RuleContext): TSTools | null;
|
|
15
|
+
export declare function getTypeScript(context: RuleContext): TypeScript | undefined;
|
|
16
|
+
export declare function isTruthyLiteral(type: TS.Type, tsTools: TSTools): boolean;
|
|
17
|
+
export declare function isFalsyType(type: TS.Type, tsTools: TSTools): boolean;
|
|
18
|
+
export declare function isNullishType(type: TS.Type, ts: TypeScript): boolean;
|
|
19
|
+
export declare function isNullableType(type: TS.Type, ts: TypeScript): boolean;
|
|
20
|
+
export declare function isBooleanLiteralType(type: TS.Type, ts: TypeScript): boolean;
|
|
21
|
+
export declare function isObjectType(type: TS.Type, ts: TypeScript): type is TS.ObjectType;
|
|
22
|
+
export declare function isReferenceObjectType(type: TS.Type, ts: TypeScript): type is TS.TypeReference;
|
|
23
|
+
export declare function isTupleObjectType(type: TS.Type, ts: TypeScript): type is TS.TupleType;
|
|
24
|
+
export declare function isTupleType(type: TS.Type, ts: TypeScript): boolean;
|
|
25
|
+
export declare function isAnyType(type: TS.Type, ts: TypeScript): boolean;
|
|
26
|
+
export declare function isUnknownType(type: TS.Type, ts: TypeScript): boolean;
|
|
27
|
+
export declare function isNeverType(type: TS.Type, ts: TypeScript): boolean;
|
|
28
|
+
export declare function isUndefinedType(type: TS.Type, ts: TypeScript): boolean;
|
|
29
|
+
export declare function isVoidType(type: TS.Type, ts: TypeScript): boolean;
|
|
30
|
+
export declare function isNullType(type: TS.Type, ts: TypeScript): boolean;
|
|
31
|
+
export declare function isPossiblyFalsyType(type: TS.Type, ts: TypeScript): boolean;
|
|
32
|
+
export declare function getCallSignaturesOfType(type: TS.Type): readonly TS.Signature[];
|
|
33
|
+
export declare function getConstrainedTypeAtLocation(checker: TS.TypeChecker, node: TS.Node): TS.Type;
|
|
34
|
+
export declare function getTypeName(type: TS.Type, tsTools: TSTools): string;
|
|
35
|
+
export declare function getTypeOfPropertyOfType(type: TS.Type, name: string, checker: TS.TypeChecker): TS.Type | undefined;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getTypeOfPropertyOfType = exports.getTypeName = exports.getConstrainedTypeAtLocation = exports.getCallSignaturesOfType = exports.isPossiblyFalsyType = exports.isNullType = exports.isVoidType = exports.isUndefinedType = exports.isNeverType = exports.isUnknownType = exports.isAnyType = exports.isTupleType = exports.isTupleObjectType = exports.isReferenceObjectType = exports.isObjectType = exports.isBooleanLiteralType = exports.isNullableType = exports.isNullishType = exports.isFalsyType = exports.isTruthyLiteral = exports.getTypeScript = exports.getTypeScriptTools = void 0;
|
|
7
|
+
const module_1 = __importDefault(require("module"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function getTypeScriptTools(context) {
|
|
10
|
+
const ts = getTypeScript(context);
|
|
11
|
+
if (!ts) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const { program, esTreeNodeToTSNodeMap, tsNodeToESTreeNodeMap } = context.parserServices;
|
|
15
|
+
if (!program || !esTreeNodeToTSNodeMap || !tsNodeToESTreeNodeMap) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const hasFullTypeInformation = context.parserServices.hasFullTypeInformation ?? true;
|
|
19
|
+
if (!hasFullTypeInformation) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
service: {
|
|
24
|
+
esTreeNodeToTSNodeMap,
|
|
25
|
+
tsNodeToESTreeNodeMap,
|
|
26
|
+
hasFullTypeInformation,
|
|
27
|
+
program,
|
|
28
|
+
},
|
|
29
|
+
ts,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
exports.getTypeScriptTools = getTypeScriptTools;
|
|
33
|
+
let cacheTypeScript;
|
|
34
|
+
function getTypeScript(context) {
|
|
35
|
+
if (cacheTypeScript) {
|
|
36
|
+
return cacheTypeScript;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const cwd = context.getCwd?.() ?? process.cwd();
|
|
40
|
+
const relativeTo = path_1.default.join(cwd, "__placeholder__.js");
|
|
41
|
+
cacheTypeScript = module_1.default.createRequire(relativeTo)("typescript");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
}
|
|
45
|
+
if (cacheTypeScript) {
|
|
46
|
+
return cacheTypeScript;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
cacheTypeScript ?? (cacheTypeScript = require("typescript"));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
}
|
|
53
|
+
return cacheTypeScript;
|
|
54
|
+
}
|
|
55
|
+
exports.getTypeScript = getTypeScript;
|
|
56
|
+
function isTruthyLiteral(type, tsTools) {
|
|
57
|
+
if (type.isUnion()) {
|
|
58
|
+
return type.types.every((t) => isTruthyLiteral(t, tsTools));
|
|
59
|
+
}
|
|
60
|
+
return ((isBooleanLiteralType(type, tsTools.ts) &&
|
|
61
|
+
tsTools.service.program.getTypeChecker().typeToString(type) === "true") ||
|
|
62
|
+
(type.isLiteral() && Boolean(type.value)));
|
|
63
|
+
}
|
|
64
|
+
exports.isTruthyLiteral = isTruthyLiteral;
|
|
65
|
+
function isFalsyType(type, tsTools) {
|
|
66
|
+
if (type.isUnion()) {
|
|
67
|
+
return type.types.every((t) => isFalsyType(t, tsTools));
|
|
68
|
+
}
|
|
69
|
+
if (isUndefinedType(type, tsTools.ts) ||
|
|
70
|
+
isNullType(type, tsTools.ts) ||
|
|
71
|
+
isVoidType(type, tsTools.ts))
|
|
72
|
+
return true;
|
|
73
|
+
if (type.isLiteral())
|
|
74
|
+
return !type.value;
|
|
75
|
+
return (isBooleanLiteralType(type, tsTools.ts) &&
|
|
76
|
+
tsTools.service.program.getTypeChecker().typeToString(type) === "false");
|
|
77
|
+
}
|
|
78
|
+
exports.isFalsyType = isFalsyType;
|
|
79
|
+
function isNullishType(type, ts) {
|
|
80
|
+
if (type.isUnion()) {
|
|
81
|
+
return type.types.every((t) => isNullishType(t, ts));
|
|
82
|
+
}
|
|
83
|
+
return isNullType(type, ts) || isUndefinedType(type, ts);
|
|
84
|
+
}
|
|
85
|
+
exports.isNullishType = isNullishType;
|
|
86
|
+
function isNullableType(type, ts) {
|
|
87
|
+
if (type.isUnion()) {
|
|
88
|
+
return type.types.some((t) => isNullableType(t, ts));
|
|
89
|
+
}
|
|
90
|
+
return isNullType(type, ts) || isUndefinedType(type, ts);
|
|
91
|
+
}
|
|
92
|
+
exports.isNullableType = isNullableType;
|
|
93
|
+
function isBooleanLiteralType(type, ts) {
|
|
94
|
+
return (type.flags & ts.TypeFlags.BooleanLiteral) !== 0;
|
|
95
|
+
}
|
|
96
|
+
exports.isBooleanLiteralType = isBooleanLiteralType;
|
|
97
|
+
function isObjectType(type, ts) {
|
|
98
|
+
return (type.flags & ts.TypeFlags.Object) !== 0;
|
|
99
|
+
}
|
|
100
|
+
exports.isObjectType = isObjectType;
|
|
101
|
+
function isReferenceObjectType(type, ts) {
|
|
102
|
+
return (isObjectType(type, ts) &&
|
|
103
|
+
(type.objectFlags & ts.ObjectFlags.Reference) !== 0);
|
|
104
|
+
}
|
|
105
|
+
exports.isReferenceObjectType = isReferenceObjectType;
|
|
106
|
+
function isTupleObjectType(type, ts) {
|
|
107
|
+
return (isObjectType(type, ts) && (type.objectFlags & ts.ObjectFlags.Tuple) !== 0);
|
|
108
|
+
}
|
|
109
|
+
exports.isTupleObjectType = isTupleObjectType;
|
|
110
|
+
function isTupleType(type, ts) {
|
|
111
|
+
return (isTupleObjectType(type, ts) ||
|
|
112
|
+
(isReferenceObjectType(type, ts) && isTupleObjectType(type.target, ts)));
|
|
113
|
+
}
|
|
114
|
+
exports.isTupleType = isTupleType;
|
|
115
|
+
function isAnyType(type, ts) {
|
|
116
|
+
return (type.flags & ts.TypeFlags.Any) !== 0;
|
|
117
|
+
}
|
|
118
|
+
exports.isAnyType = isAnyType;
|
|
119
|
+
function isUnknownType(type, ts) {
|
|
120
|
+
return (type.flags & ts.TypeFlags.Unknown) !== 0;
|
|
121
|
+
}
|
|
122
|
+
exports.isUnknownType = isUnknownType;
|
|
123
|
+
function isNeverType(type, ts) {
|
|
124
|
+
return (type.flags & ts.TypeFlags.Never) !== 0;
|
|
125
|
+
}
|
|
126
|
+
exports.isNeverType = isNeverType;
|
|
127
|
+
function isUndefinedType(type, ts) {
|
|
128
|
+
return (type.flags & ts.TypeFlags.Undefined) !== 0;
|
|
129
|
+
}
|
|
130
|
+
exports.isUndefinedType = isUndefinedType;
|
|
131
|
+
function isVoidType(type, ts) {
|
|
132
|
+
return (type.flags & ts.TypeFlags.Void) !== 0;
|
|
133
|
+
}
|
|
134
|
+
exports.isVoidType = isVoidType;
|
|
135
|
+
function isNullType(type, ts) {
|
|
136
|
+
return (type.flags & ts.TypeFlags.Null) !== 0;
|
|
137
|
+
}
|
|
138
|
+
exports.isNullType = isNullType;
|
|
139
|
+
function isPossiblyFalsyType(type, ts) {
|
|
140
|
+
if (type.isUnion()) {
|
|
141
|
+
return type.types.some((t) => isPossiblyFalsyType(t, ts));
|
|
142
|
+
}
|
|
143
|
+
return (type.flags & ts.TypeFlags.PossiblyFalsy) !== 0;
|
|
144
|
+
}
|
|
145
|
+
exports.isPossiblyFalsyType = isPossiblyFalsyType;
|
|
146
|
+
function getCallSignaturesOfType(type) {
|
|
147
|
+
if (type.isUnion()) {
|
|
148
|
+
return type.types.flatMap((t) => getCallSignaturesOfType(t));
|
|
149
|
+
}
|
|
150
|
+
if (type.isIntersection()) {
|
|
151
|
+
let signatures = [];
|
|
152
|
+
for (const t of type.types) {
|
|
153
|
+
const sig = getCallSignaturesOfType(t);
|
|
154
|
+
if (sig.length !== 0) {
|
|
155
|
+
if (signatures.length) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
signatures = sig;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return signatures;
|
|
162
|
+
}
|
|
163
|
+
return type.getCallSignatures();
|
|
164
|
+
}
|
|
165
|
+
exports.getCallSignaturesOfType = getCallSignaturesOfType;
|
|
166
|
+
function getConstrainedTypeAtLocation(checker, node) {
|
|
167
|
+
const nodeType = checker.getTypeAtLocation(node);
|
|
168
|
+
const constrained = checker.getBaseConstraintOfType(nodeType);
|
|
169
|
+
return constrained ?? nodeType;
|
|
170
|
+
}
|
|
171
|
+
exports.getConstrainedTypeAtLocation = getConstrainedTypeAtLocation;
|
|
172
|
+
function getTypeName(type, tsTools) {
|
|
173
|
+
const { ts } = tsTools;
|
|
174
|
+
if ((type.flags & ts.TypeFlags.StringLike) !== 0) {
|
|
175
|
+
return "string";
|
|
176
|
+
}
|
|
177
|
+
const typeChecker = tsTools.service.program.getTypeChecker();
|
|
178
|
+
if ((type.flags & ts.TypeFlags.TypeParameter) !== 0) {
|
|
179
|
+
const symbol = type.getSymbol();
|
|
180
|
+
const decls = symbol?.getDeclarations();
|
|
181
|
+
const typeParamDecl = decls?.[0];
|
|
182
|
+
if (ts.isTypeParameterDeclaration(typeParamDecl) &&
|
|
183
|
+
typeParamDecl.constraint != null) {
|
|
184
|
+
return getTypeName(typeChecker.getTypeFromTypeNode(typeParamDecl.constraint), tsTools);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (type.isUnion() &&
|
|
188
|
+
type.types
|
|
189
|
+
.map((value) => getTypeName(value, tsTools))
|
|
190
|
+
.every((t) => t === "string")) {
|
|
191
|
+
return "string";
|
|
192
|
+
}
|
|
193
|
+
if (type.isIntersection() &&
|
|
194
|
+
type.types
|
|
195
|
+
.map((value) => getTypeName(value, tsTools))
|
|
196
|
+
.some((t) => t === "string")) {
|
|
197
|
+
return "string";
|
|
198
|
+
}
|
|
199
|
+
return typeChecker.typeToString(type);
|
|
200
|
+
}
|
|
201
|
+
exports.getTypeName = getTypeName;
|
|
202
|
+
function getTypeOfPropertyOfType(type, name, checker) {
|
|
203
|
+
return checker.getTypeOfPropertyOfType(type, name);
|
|
204
|
+
}
|
|
205
|
+
exports.getTypeOfPropertyOfType = getTypeOfPropertyOfType;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-svelte",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "ESLint plugin for Svelte using AST",
|
|
5
5
|
"repository": "git+https://github.com/ota-meshi/eslint-plugin-svelte.git",
|
|
6
6
|
"homepage": "https://ota-meshi.github.io/eslint-plugin-svelte",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"docs:preview": "yarn svelte-kit preview",
|
|
36
36
|
"docs:watch": "yarn svelte-kit dev",
|
|
37
37
|
"format-for-gen-file": "eslint src/types-for-node.ts src/utils/rules.ts src/configs --fix",
|
|
38
|
+
"install-with-ignore-engines": "yarn --ignore-engines",
|
|
38
39
|
"lint": "run-p lint:*",
|
|
39
40
|
"lint-fix": "yarn lint:es --fix && yarn lint:style --fix",
|
|
40
41
|
"lint:es": "eslint --cache -f friendly .",
|
|
@@ -87,10 +88,10 @@
|
|
|
87
88
|
"@changesets/changelog-github": "^0.4.6",
|
|
88
89
|
"@changesets/cli": "^2.24.2",
|
|
89
90
|
"@fontsource/fira-mono": "^4.5.0",
|
|
90
|
-
"@ota-meshi/eslint-plugin": "^0.
|
|
91
|
+
"@ota-meshi/eslint-plugin": "^0.12.0",
|
|
91
92
|
"@sindresorhus/slugify": "^2.1.0",
|
|
92
|
-
"@sveltejs/adapter-static": "^1.0.0-next.
|
|
93
|
-
"@sveltejs/kit": "^1.0.0-next.
|
|
93
|
+
"@sveltejs/adapter-static": "^1.0.0-next.40",
|
|
94
|
+
"@sveltejs/kit": "^1.0.0-next.456",
|
|
94
95
|
"@types/babel__core": "^7.1.19",
|
|
95
96
|
"@types/cross-spawn": "^6.0.2",
|
|
96
97
|
"@types/escape-html": "^1.0.2",
|
|
@@ -111,6 +112,7 @@
|
|
|
111
112
|
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
|
112
113
|
"@typescript-eslint/parser": "^5.4.1-0",
|
|
113
114
|
"@typescript-eslint/parser-v4": "npm:@typescript-eslint/parser@4",
|
|
115
|
+
"@typescript/vfs": "^1.4.0",
|
|
114
116
|
"assert": "^2.0.0",
|
|
115
117
|
"commitlint": "^17.0.3",
|
|
116
118
|
"env-cmd": "^10.1.0",
|
|
@@ -161,10 +163,10 @@
|
|
|
161
163
|
"svelte-adapter-ghpages": "0.0.2",
|
|
162
164
|
"type-coverage": "^2.22.0",
|
|
163
165
|
"typescript": "^4.5.2",
|
|
164
|
-
"vite": "^3.
|
|
166
|
+
"vite": "^3.1.0-0",
|
|
165
167
|
"vite-plugin-svelte-md": "^0.1.5",
|
|
166
168
|
"yaml": "^2.1.1",
|
|
167
|
-
"yarn-deduplicate": "^
|
|
169
|
+
"yarn-deduplicate": "^6.0.0"
|
|
168
170
|
},
|
|
169
171
|
"publishConfig": {
|
|
170
172
|
"access": "public"
|