eslint-plugin-svelte 3.12.2 → 3.12.4
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 +6 -0
- package/lib/main.d.ts +1 -1
- package/lib/meta.d.ts +1 -1
- package/lib/meta.js +1 -1
- package/lib/rules/no-dynamic-slot-name.js +6 -7
- package/lib/rules/no-immutable-reactive-statements.js +12 -10
- package/lib/rules/no-navigation-without-resolve.js +59 -33
- package/lib/rules/no-not-function-handler.js +6 -7
- package/lib/rules/no-top-level-browser-globals.js +17 -6
- package/lib/rules/no-unused-props.js +3 -1
- package/lib/rules/valid-style-parse.js +8 -2
- package/lib/utils/ast-utils.d.ts +11 -0
- package/lib/utils/ast-utils.js +19 -0
- package/lib/utils/expression-affixes.js +26 -16
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -85,6 +85,9 @@ export default [
|
|
|
85
85
|
// By doing so, some rules in eslint-plugin-svelte will automatically read the configuration and adjust their behavior accordingly.
|
|
86
86
|
// While certain Svelte settings may be statically loaded from svelte.config.js even if you don’t specify it,
|
|
87
87
|
// explicitly specifying it ensures better compatibility and functionality.
|
|
88
|
+
//
|
|
89
|
+
// If non-serializable properties are included, running ESLint with the --cache flag will fail.
|
|
90
|
+
// In that case, please remove the non-serializable properties. (e.g. `svelteConfig: { ...svelteConfig, kit: { ...svelteConfig.kit, typescript: undefined }}`)
|
|
88
91
|
svelteConfig
|
|
89
92
|
}
|
|
90
93
|
}
|
|
@@ -143,6 +146,9 @@ export default ts.config(
|
|
|
143
146
|
// By doing so, some rules in eslint-plugin-svelte will automatically read the configuration and adjust their behavior accordingly.
|
|
144
147
|
// While certain Svelte settings may be statically loaded from svelte.config.js even if you don’t specify it,
|
|
145
148
|
// explicitly specifying it ensures better compatibility and functionality.
|
|
149
|
+
//
|
|
150
|
+
// If non-serializable properties are included, running ESLint with the --cache flag will fail.
|
|
151
|
+
// In that case, please remove the non-serializable properties. (e.g. `svelteConfig: { ...svelteConfig, kit: { ...svelteConfig.kit, typescript: undefined }}`)
|
|
146
152
|
svelteConfig
|
|
147
153
|
}
|
|
148
154
|
}
|
package/lib/main.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare const configs: {
|
|
|
14
14
|
export declare const rules: Record<string, Rule.RuleModule>;
|
|
15
15
|
export declare const meta: {
|
|
16
16
|
name: "eslint-plugin-svelte";
|
|
17
|
-
version: "3.12.
|
|
17
|
+
version: "3.12.4";
|
|
18
18
|
};
|
|
19
19
|
export declare const processors: {
|
|
20
20
|
'.svelte': typeof processor;
|
package/lib/meta.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const name: "eslint-plugin-svelte";
|
|
2
|
-
export declare const version: "3.12.
|
|
2
|
+
export declare const version: "3.12.4";
|
package/lib/meta.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRule } from '../utils/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { FindVariableContext, getAttributeValueQuoteAndRange, getStringIfConstant } from '../utils/ast-utils.js';
|
|
3
3
|
export default createRule('no-dynamic-slot-name', {
|
|
4
4
|
meta: {
|
|
5
5
|
docs: {
|
|
@@ -56,16 +56,15 @@ export default createRule('no-dynamic-slot-name', {
|
|
|
56
56
|
* Get static text from given expression
|
|
57
57
|
*/
|
|
58
58
|
function getStaticText(node) {
|
|
59
|
-
const expr = findRootExpression(node);
|
|
59
|
+
const expr = findRootExpression(new FindVariableContext(context), node);
|
|
60
60
|
return getStringIfConstant(expr);
|
|
61
61
|
}
|
|
62
62
|
/** Find data expression */
|
|
63
|
-
function findRootExpression(
|
|
64
|
-
if (node.type !== 'Identifier'
|
|
63
|
+
function findRootExpression(ctx, node) {
|
|
64
|
+
if (node.type !== 'Identifier') {
|
|
65
65
|
return node;
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
const variable = findVariable(context, node);
|
|
67
|
+
const variable = ctx.findVariable(node);
|
|
69
68
|
if (!variable || variable.defs.length !== 1) {
|
|
70
69
|
return node;
|
|
71
70
|
}
|
|
@@ -73,7 +72,7 @@ export default createRule('no-dynamic-slot-name', {
|
|
|
73
72
|
if (def.type === 'Variable') {
|
|
74
73
|
if (def.parent.kind === 'const' && def.node.init) {
|
|
75
74
|
const init = def.node.init;
|
|
76
|
-
return findRootExpression(
|
|
75
|
+
return findRootExpression(ctx, init);
|
|
77
76
|
}
|
|
78
77
|
}
|
|
79
78
|
return node;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRule } from '../utils/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { FindVariableContext, iterateIdentifiers } from '../utils/ast-utils.js';
|
|
3
3
|
export default createRule('no-immutable-reactive-statements', {
|
|
4
4
|
meta: {
|
|
5
5
|
docs: {
|
|
@@ -81,7 +81,7 @@ export default createRule('no-immutable-reactive-statements', {
|
|
|
81
81
|
return true;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
return hasWrite(variable);
|
|
84
|
+
return hasWrite(new FindVariableContext(context), variable);
|
|
85
85
|
}
|
|
86
86
|
return false;
|
|
87
87
|
});
|
|
@@ -89,7 +89,7 @@ export default createRule('no-immutable-reactive-statements', {
|
|
|
89
89
|
return isMutableDefine;
|
|
90
90
|
}
|
|
91
91
|
/** Checks whether the given variable has a write or reactive store reference or not. */
|
|
92
|
-
function hasWrite(variable) {
|
|
92
|
+
function hasWrite(ctx, variable) {
|
|
93
93
|
const defIds = variable.defs.map((def) => def.name);
|
|
94
94
|
for (const reference of variable.references) {
|
|
95
95
|
if (reference.isWrite() &&
|
|
@@ -97,14 +97,14 @@ export default createRule('no-immutable-reactive-statements', {
|
|
|
97
97
|
reference.identifier.range[1] <= defId.range[1])) {
|
|
98
98
|
return true;
|
|
99
99
|
}
|
|
100
|
-
if (hasWriteMember(reference.identifier)) {
|
|
100
|
+
if (hasWriteMember(ctx, reference.identifier)) {
|
|
101
101
|
return true;
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
/** Checks whether the given expression has writing to a member or not. */
|
|
107
|
-
function hasWriteMember(expr) {
|
|
107
|
+
function hasWriteMember(ctx, expr) {
|
|
108
108
|
if (expr.type === 'JSXIdentifier')
|
|
109
109
|
return false;
|
|
110
110
|
const parent = expr.parent;
|
|
@@ -118,21 +118,23 @@ export default createRule('no-immutable-reactive-statements', {
|
|
|
118
118
|
return parent.operator === 'delete' && parent.argument === expr;
|
|
119
119
|
}
|
|
120
120
|
if (parent.type === 'MemberExpression') {
|
|
121
|
-
return parent.object === expr && hasWriteMember(parent);
|
|
121
|
+
return parent.object === expr && hasWriteMember(ctx, parent);
|
|
122
122
|
}
|
|
123
123
|
if (parent.type === 'SvelteDirective') {
|
|
124
124
|
return parent.kind === 'Binding' && parent.expression === expr;
|
|
125
125
|
}
|
|
126
126
|
if (parent.type === 'SvelteEachBlock') {
|
|
127
|
-
return (parent.context !== null &&
|
|
127
|
+
return (parent.context !== null &&
|
|
128
|
+
parent.expression === expr &&
|
|
129
|
+
hasWriteReference(ctx, parent.context));
|
|
128
130
|
}
|
|
129
131
|
return false;
|
|
130
132
|
}
|
|
131
133
|
/** Checks whether the given pattern has writing or not. */
|
|
132
|
-
function hasWriteReference(pattern) {
|
|
134
|
+
function hasWriteReference(ctx, pattern) {
|
|
133
135
|
for (const id of iterateIdentifiers(pattern)) {
|
|
134
|
-
const variable = findVariable(
|
|
135
|
-
if (variable && hasWrite(variable))
|
|
136
|
+
const variable = ctx.findVariable(id);
|
|
137
|
+
if (variable && hasWrite(ctx, variable))
|
|
136
138
|
return true;
|
|
137
139
|
}
|
|
138
140
|
return false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRule } from '../utils/index.js';
|
|
2
2
|
import { ReferenceTracker } from '@eslint-community/eslint-utils';
|
|
3
|
+
import { FindVariableContext } from '../utils/ast-utils.js';
|
|
3
4
|
import { findVariable } from '../utils/ast-utils.js';
|
|
4
5
|
export default createRule('no-navigation-without-resolve', {
|
|
5
6
|
meta: {
|
|
@@ -74,12 +75,12 @@ export default createRule('no-navigation-without-resolve', {
|
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
77
|
if ((node.value[0].type === 'SvelteLiteral' &&
|
|
77
|
-
!expressionIsAbsolute(node.value[0]) &&
|
|
78
|
-
!expressionIsFragment(node.value[0])) ||
|
|
78
|
+
!expressionIsAbsolute(new FindVariableContext(context), node.value[0]) &&
|
|
79
|
+
!expressionIsFragment(new FindVariableContext(context), node.value[0])) ||
|
|
79
80
|
(node.value[0].type === 'SvelteMustacheTag' &&
|
|
80
|
-
!expressionIsAbsolute(node.value[0].expression) &&
|
|
81
|
-
!expressionIsFragment(node.value[0].expression) &&
|
|
82
|
-
!isResolveCall(context, node.value[0].expression, resolveReferences))) {
|
|
81
|
+
!expressionIsAbsolute(new FindVariableContext(context), node.value[0].expression) &&
|
|
82
|
+
!expressionIsFragment(new FindVariableContext(context), node.value[0].expression) &&
|
|
83
|
+
!isResolveCall(new FindVariableContext(context), node.value[0].expression, resolveReferences))) {
|
|
83
84
|
context.report({ loc: node.value[0].loc, messageId: 'linkWithoutResolve' });
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -149,7 +150,7 @@ function checkGotoCall(context, call, resolveReferences) {
|
|
|
149
150
|
return;
|
|
150
151
|
}
|
|
151
152
|
const url = call.arguments[0];
|
|
152
|
-
if (!isResolveCall(context, url, resolveReferences)) {
|
|
153
|
+
if (!isResolveCall(new FindVariableContext(context), url, resolveReferences)) {
|
|
153
154
|
context.report({ loc: url.loc, messageId: 'gotoWithoutResolve' });
|
|
154
155
|
}
|
|
155
156
|
}
|
|
@@ -158,12 +159,13 @@ function checkShallowNavigationCall(context, call, resolveReferences, messageId)
|
|
|
158
159
|
return;
|
|
159
160
|
}
|
|
160
161
|
const url = call.arguments[0];
|
|
161
|
-
if (!expressionIsEmpty(url) &&
|
|
162
|
+
if (!expressionIsEmpty(url) &&
|
|
163
|
+
!isResolveCall(new FindVariableContext(context), url, resolveReferences)) {
|
|
162
164
|
context.report({ loc: url.loc, messageId });
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
// Helper functions
|
|
166
|
-
function isResolveCall(
|
|
168
|
+
function isResolveCall(ctx, node, resolveReferences) {
|
|
167
169
|
if (node.type === 'CallExpression' &&
|
|
168
170
|
((node.callee.type === 'Identifier' && resolveReferences.has(node.callee)) ||
|
|
169
171
|
(node.callee.type === 'MemberExpression' &&
|
|
@@ -171,17 +173,17 @@ function isResolveCall(context, node, resolveReferences) {
|
|
|
171
173
|
resolveReferences.has(node.callee.property)))) {
|
|
172
174
|
return true;
|
|
173
175
|
}
|
|
174
|
-
if (node.type
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
176
|
+
if (node.type !== 'Identifier') {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
const variable = ctx.findVariable(node);
|
|
180
|
+
if (variable === null ||
|
|
181
|
+
variable.identifiers.length === 0 ||
|
|
182
|
+
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
|
|
183
|
+
variable.identifiers[0].parent.init === null) {
|
|
184
|
+
return false;
|
|
183
185
|
}
|
|
184
|
-
return
|
|
186
|
+
return isResolveCall(ctx, variable.identifiers[0].parent.init, resolveReferences);
|
|
185
187
|
}
|
|
186
188
|
function expressionIsEmpty(url) {
|
|
187
189
|
return ((url.type === 'Literal' && url.value === '') ||
|
|
@@ -190,50 +192,74 @@ function expressionIsEmpty(url) {
|
|
|
190
192
|
url.quasis.length === 1 &&
|
|
191
193
|
url.quasis[0].value.raw === ''));
|
|
192
194
|
}
|
|
193
|
-
function expressionIsAbsolute(url) {
|
|
195
|
+
function expressionIsAbsolute(ctx, url) {
|
|
194
196
|
switch (url.type) {
|
|
195
197
|
case 'BinaryExpression':
|
|
196
|
-
return binaryExpressionIsAbsolute(url);
|
|
198
|
+
return binaryExpressionIsAbsolute(ctx, url);
|
|
199
|
+
case 'Identifier':
|
|
200
|
+
return identifierIsAbsolute(ctx, url);
|
|
197
201
|
case 'Literal':
|
|
198
202
|
return typeof url.value === 'string' && urlValueIsAbsolute(url.value);
|
|
199
203
|
case 'SvelteLiteral':
|
|
200
204
|
return urlValueIsAbsolute(url.value);
|
|
201
205
|
case 'TemplateLiteral':
|
|
202
|
-
return templateLiteralIsAbsolute(url);
|
|
206
|
+
return templateLiteralIsAbsolute(ctx, url);
|
|
203
207
|
default:
|
|
204
208
|
return false;
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
|
-
function binaryExpressionIsAbsolute(url) {
|
|
208
|
-
return ((url.left.type !== 'PrivateIdentifier' && expressionIsAbsolute(url.left)) ||
|
|
209
|
-
expressionIsAbsolute(url.right));
|
|
211
|
+
function binaryExpressionIsAbsolute(ctx, url) {
|
|
212
|
+
return ((url.left.type !== 'PrivateIdentifier' && expressionIsAbsolute(ctx, url.left)) ||
|
|
213
|
+
expressionIsAbsolute(ctx, url.right));
|
|
214
|
+
}
|
|
215
|
+
function identifierIsAbsolute(ctx, url) {
|
|
216
|
+
const variable = ctx.findVariable(url);
|
|
217
|
+
if (variable === null ||
|
|
218
|
+
variable.identifiers.length === 0 ||
|
|
219
|
+
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
|
|
220
|
+
variable.identifiers[0].parent.init === null) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
return expressionIsAbsolute(ctx, variable.identifiers[0].parent.init);
|
|
210
224
|
}
|
|
211
|
-
function templateLiteralIsAbsolute(url) {
|
|
212
|
-
return (url.expressions.some(expressionIsAbsolute) ||
|
|
225
|
+
function templateLiteralIsAbsolute(ctx, url) {
|
|
226
|
+
return (url.expressions.some((expression) => expressionIsAbsolute(ctx, expression)) ||
|
|
213
227
|
url.quasis.some((quasi) => urlValueIsAbsolute(quasi.value.raw)));
|
|
214
228
|
}
|
|
215
229
|
function urlValueIsAbsolute(url) {
|
|
216
230
|
return /^[+a-z]*:/i.test(url);
|
|
217
231
|
}
|
|
218
|
-
function expressionIsFragment(url) {
|
|
232
|
+
function expressionIsFragment(ctx, url) {
|
|
219
233
|
switch (url.type) {
|
|
220
234
|
case 'BinaryExpression':
|
|
221
|
-
return binaryExpressionIsFragment(url);
|
|
235
|
+
return binaryExpressionIsFragment(ctx, url);
|
|
236
|
+
case 'Identifier':
|
|
237
|
+
return identifierIsFragment(ctx, url);
|
|
222
238
|
case 'Literal':
|
|
223
239
|
return typeof url.value === 'string' && urlValueIsFragment(url.value);
|
|
224
240
|
case 'SvelteLiteral':
|
|
225
241
|
return urlValueIsFragment(url.value);
|
|
226
242
|
case 'TemplateLiteral':
|
|
227
|
-
return templateLiteralIsFragment(url);
|
|
243
|
+
return templateLiteralIsFragment(ctx, url);
|
|
228
244
|
default:
|
|
229
245
|
return false;
|
|
230
246
|
}
|
|
231
247
|
}
|
|
232
|
-
function binaryExpressionIsFragment(url) {
|
|
233
|
-
return url.left.type !== 'PrivateIdentifier' && expressionIsFragment(url.left);
|
|
248
|
+
function binaryExpressionIsFragment(ctx, url) {
|
|
249
|
+
return url.left.type !== 'PrivateIdentifier' && expressionIsFragment(ctx, url.left);
|
|
250
|
+
}
|
|
251
|
+
function identifierIsFragment(ctx, url) {
|
|
252
|
+
const variable = ctx.findVariable(url);
|
|
253
|
+
if (variable === null ||
|
|
254
|
+
variable.identifiers.length === 0 ||
|
|
255
|
+
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
|
|
256
|
+
variable.identifiers[0].parent.init === null) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
return expressionIsFragment(ctx, variable.identifiers[0].parent.init);
|
|
234
260
|
}
|
|
235
|
-
function templateLiteralIsFragment(url) {
|
|
236
|
-
return ((url.expressions.length >= 1 && expressionIsFragment(url.expressions[0])) ||
|
|
261
|
+
function templateLiteralIsFragment(ctx, url) {
|
|
262
|
+
return ((url.expressions.length >= 1 && expressionIsFragment(ctx, url.expressions[0])) ||
|
|
237
263
|
(url.quasis.length >= 1 && urlValueIsFragment(url.quasis[0].value.raw)));
|
|
238
264
|
}
|
|
239
265
|
function urlValueIsFragment(url) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRule } from '../utils/index.js';
|
|
2
|
-
import { findVariable } from '../utils/ast-utils.js';
|
|
3
2
|
import { EVENT_NAMES } from '../utils/events.js';
|
|
3
|
+
import { FindVariableContext } from '../utils/ast-utils.js';
|
|
4
4
|
const PHRASES = {
|
|
5
5
|
ObjectExpression: 'object',
|
|
6
6
|
ArrayExpression: 'array',
|
|
@@ -34,12 +34,11 @@ export default createRule('no-not-function-handler', {
|
|
|
34
34
|
},
|
|
35
35
|
create(context) {
|
|
36
36
|
/** Find data expression */
|
|
37
|
-
function findRootExpression(
|
|
38
|
-
if (node.type !== 'Identifier'
|
|
37
|
+
function findRootExpression(ctx, node) {
|
|
38
|
+
if (node.type !== 'Identifier') {
|
|
39
39
|
return node;
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
const variable = findVariable(context, node);
|
|
41
|
+
const variable = ctx.findVariable(node);
|
|
43
42
|
if (!variable || variable.defs.length !== 1) {
|
|
44
43
|
return node;
|
|
45
44
|
}
|
|
@@ -47,7 +46,7 @@ export default createRule('no-not-function-handler', {
|
|
|
47
46
|
if (def.type === 'Variable') {
|
|
48
47
|
if (def.parent.kind === 'const' && def.node.init) {
|
|
49
48
|
const init = def.node.init;
|
|
50
|
-
return findRootExpression(
|
|
49
|
+
return findRootExpression(ctx, init);
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
return node;
|
|
@@ -57,7 +56,7 @@ export default createRule('no-not-function-handler', {
|
|
|
57
56
|
if (!node) {
|
|
58
57
|
return;
|
|
59
58
|
}
|
|
60
|
-
const expression = findRootExpression(node);
|
|
59
|
+
const expression = findRootExpression(new FindVariableContext(context), node);
|
|
61
60
|
if (expression.type !== 'ObjectExpression' &&
|
|
62
61
|
expression.type !== 'ArrayExpression' &&
|
|
63
62
|
expression.type !== 'ClassExpression' &&
|
|
@@ -331,12 +331,23 @@ export default createRule('no-top-level-browser-globals', {
|
|
|
331
331
|
const end = pp.range[1];
|
|
332
332
|
return (n) => start <= n.range[0] && n.range[1] <= end;
|
|
333
333
|
}
|
|
334
|
-
if (
|
|
335
|
-
parent.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
334
|
+
if (parent.type === 'LogicalExpression') {
|
|
335
|
+
if (!guardInfo.not && parent.operator === '&&') {
|
|
336
|
+
const parentChecker = getGuardChecker({ not: guardInfo.not, node: parent });
|
|
337
|
+
if (parent.left === guardInfo.node) {
|
|
338
|
+
const block = parent.right;
|
|
339
|
+
return (n) => {
|
|
340
|
+
if (parentChecker?.(n)) {
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
return block.range[0] <= n.range[0] && n.range[1] <= block.range[1];
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return parentChecker;
|
|
347
|
+
}
|
|
348
|
+
if (guardInfo.not && parent.operator === '||') {
|
|
349
|
+
return getGuardChecker({ not: guardInfo.not, node: parent });
|
|
350
|
+
}
|
|
340
351
|
}
|
|
341
352
|
return null;
|
|
342
353
|
}
|
|
@@ -2,6 +2,7 @@ import { createRule } from '../utils/index.js';
|
|
|
2
2
|
import { getTypeScriptTools, isAnyType } from '../utils/ts-utils/index.js';
|
|
3
3
|
import { findVariable } from '../utils/ast-utils.js';
|
|
4
4
|
import { toRegExp } from '../utils/regexp.js';
|
|
5
|
+
import { normalize } from 'path';
|
|
5
6
|
let isRemovedWarningShown = false;
|
|
6
7
|
export default createRule('no-unused-props', {
|
|
7
8
|
meta: {
|
|
@@ -100,7 +101,8 @@ export default createRule('no-unused-props', {
|
|
|
100
101
|
const declarations = symbol.getDeclarations();
|
|
101
102
|
if (!declarations || declarations.length === 0)
|
|
102
103
|
return false;
|
|
103
|
-
|
|
104
|
+
// TypeScript declaration file name is normalized to support Windows style paths
|
|
105
|
+
return declarations.every((decl) => normalize(decl.getSourceFile().fileName) === fileName);
|
|
104
106
|
}
|
|
105
107
|
/**
|
|
106
108
|
* Extracts property paths from member expressions.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRule } from '../utils/index.js';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
export default createRule('valid-style-parse', {
|
|
3
4
|
meta: {
|
|
4
5
|
docs: {
|
|
@@ -15,14 +16,19 @@ export default createRule('valid-style-parse', {
|
|
|
15
16
|
if (!sourceCode.parserServices.isSvelte) {
|
|
16
17
|
return {};
|
|
17
18
|
}
|
|
18
|
-
const cwd = `${context.cwd ?? process.cwd()}
|
|
19
|
+
const cwd = `${context.cwd ?? process.cwd()}${path.sep}`;
|
|
19
20
|
return {
|
|
20
21
|
SvelteStyleElement(node) {
|
|
21
22
|
const styleContext = sourceCode.parserServices.getStyleContext();
|
|
22
23
|
if (styleContext.status === 'parse-error') {
|
|
24
|
+
// This will replace backslashes to forward slashes only
|
|
25
|
+
// if Node.js reports Windows style path separators
|
|
26
|
+
let message = styleContext.error.message.replace(cwd, '');
|
|
27
|
+
if (path.sep === '\\')
|
|
28
|
+
message = message.replace(/\\/g, '/');
|
|
23
29
|
context.report({
|
|
24
30
|
loc: node.loc,
|
|
25
|
-
message: `Error parsing style element. Error message: "${
|
|
31
|
+
message: `Error parsing style element. Error message: "${message}"`
|
|
26
32
|
});
|
|
27
33
|
}
|
|
28
34
|
if (styleContext.status === 'unknown-lang') {
|
package/lib/utils/ast-utils.d.ts
CHANGED
|
@@ -62,6 +62,17 @@ export declare function getLangValue(node: SvAST.SvelteScriptElement | SvAST.Sve
|
|
|
62
62
|
* Find the variable of a given name.
|
|
63
63
|
*/
|
|
64
64
|
export declare function findVariable(context: RuleContext, node: TSESTree.Identifier): Variable | null;
|
|
65
|
+
/**
|
|
66
|
+
* Context for safely finding variables, avoiding infinite recursion.
|
|
67
|
+
* This should be used when the caller function may be called recursively, instead of `findVariable()`.
|
|
68
|
+
*
|
|
69
|
+
* Create an instance of this class at the top of the call stack where you want to start searching for identifiers,
|
|
70
|
+
* and then use it within the recursion.
|
|
71
|
+
*/
|
|
72
|
+
export declare class FindVariableContext {
|
|
73
|
+
readonly findVariable: (node: TSESTree.Identifier) => Variable | null;
|
|
74
|
+
constructor(context: RuleContext);
|
|
75
|
+
}
|
|
65
76
|
/**
|
|
66
77
|
* Iterate the identifiers of a given pattern node.
|
|
67
78
|
*/
|
package/lib/utils/ast-utils.js
CHANGED
|
@@ -172,6 +172,25 @@ export function findVariable(context, node) {
|
|
|
172
172
|
// Remove the $ and search for the variable again, as it may be a store access variable.
|
|
173
173
|
return eslintUtils.findVariable(initialScope, node.name.slice(1));
|
|
174
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Context for safely finding variables, avoiding infinite recursion.
|
|
177
|
+
* This should be used when the caller function may be called recursively, instead of `findVariable()`.
|
|
178
|
+
*
|
|
179
|
+
* Create an instance of this class at the top of the call stack where you want to start searching for identifiers,
|
|
180
|
+
* and then use it within the recursion.
|
|
181
|
+
*/
|
|
182
|
+
export class FindVariableContext {
|
|
183
|
+
constructor(context) {
|
|
184
|
+
const visited = new Set();
|
|
185
|
+
this.findVariable = (node) => {
|
|
186
|
+
if (visited.has(node)) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
visited.add(node);
|
|
190
|
+
return findVariable(context, node);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
175
194
|
/**
|
|
176
195
|
* Iterate the identifiers of a given pattern node.
|
|
177
196
|
*/
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Variable prefix extraction
|
|
1
|
+
import { FindVariableContext } from './ast-utils.js';
|
|
3
2
|
export function extractExpressionPrefixVariable(context, expression) {
|
|
3
|
+
return extractExpressionPrefixVariableInternal(new FindVariableContext(context), expression);
|
|
4
|
+
}
|
|
5
|
+
export function extractExpressionPrefixLiteral(context, expression) {
|
|
6
|
+
return extractExpressionPrefixLiteralInternal(new FindVariableContext(context), expression);
|
|
7
|
+
}
|
|
8
|
+
export function extractExpressionSuffixLiteral(context, expression) {
|
|
9
|
+
return extractExpressionSuffixLiteralInternal(new FindVariableContext(context), expression);
|
|
10
|
+
}
|
|
11
|
+
// Variable prefix extraction
|
|
12
|
+
function extractExpressionPrefixVariableInternal(context, expression) {
|
|
4
13
|
switch (expression.type) {
|
|
5
14
|
case 'BinaryExpression':
|
|
6
15
|
return extractBinaryExpressionPrefixVariable(context, expression);
|
|
@@ -16,18 +25,19 @@ export function extractExpressionPrefixVariable(context, expression) {
|
|
|
16
25
|
}
|
|
17
26
|
function extractBinaryExpressionPrefixVariable(context, expression) {
|
|
18
27
|
return expression.left.type !== 'PrivateIdentifier'
|
|
19
|
-
?
|
|
28
|
+
? extractExpressionPrefixVariableInternal(context, expression.left)
|
|
20
29
|
: null;
|
|
21
30
|
}
|
|
22
31
|
function extractVariablePrefixVariable(context, expression) {
|
|
23
|
-
const variable = findVariable(
|
|
32
|
+
const variable = context.findVariable(expression);
|
|
24
33
|
if (variable === null ||
|
|
25
34
|
variable.identifiers.length !== 1 ||
|
|
26
35
|
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
|
|
27
36
|
variable.identifiers[0].parent.init === null) {
|
|
28
37
|
return expression;
|
|
29
38
|
}
|
|
30
|
-
return (
|
|
39
|
+
return (extractExpressionPrefixVariableInternal(context, variable.identifiers[0].parent.init) ??
|
|
40
|
+
expression);
|
|
31
41
|
}
|
|
32
42
|
function extractMemberExpressionPrefixVariable(expression) {
|
|
33
43
|
return expression.property.type === 'Identifier' ? expression.property : null;
|
|
@@ -40,14 +50,14 @@ function extractTemplateLiteralPrefixVariable(context, expression) {
|
|
|
40
50
|
continue;
|
|
41
51
|
}
|
|
42
52
|
if (part.type !== 'TemplateElement') {
|
|
43
|
-
return
|
|
53
|
+
return extractExpressionPrefixVariableInternal(context, part);
|
|
44
54
|
}
|
|
45
55
|
return null;
|
|
46
56
|
}
|
|
47
57
|
return null;
|
|
48
58
|
}
|
|
49
59
|
// Literal prefix extraction
|
|
50
|
-
|
|
60
|
+
function extractExpressionPrefixLiteralInternal(context, expression) {
|
|
51
61
|
switch (expression.type) {
|
|
52
62
|
case 'BinaryExpression':
|
|
53
63
|
return extractBinaryExpressionPrefixLiteral(context, expression);
|
|
@@ -65,18 +75,18 @@ export function extractExpressionPrefixLiteral(context, expression) {
|
|
|
65
75
|
}
|
|
66
76
|
function extractBinaryExpressionPrefixLiteral(context, expression) {
|
|
67
77
|
return expression.left.type !== 'PrivateIdentifier'
|
|
68
|
-
?
|
|
78
|
+
? extractExpressionPrefixLiteralInternal(context, expression.left)
|
|
69
79
|
: null;
|
|
70
80
|
}
|
|
71
81
|
function extractVariablePrefixLiteral(context, expression) {
|
|
72
|
-
const variable = findVariable(
|
|
82
|
+
const variable = context.findVariable(expression);
|
|
73
83
|
if (variable === null ||
|
|
74
84
|
variable.identifiers.length !== 1 ||
|
|
75
85
|
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
|
|
76
86
|
variable.identifiers[0].parent.init === null) {
|
|
77
87
|
return null;
|
|
78
88
|
}
|
|
79
|
-
return
|
|
89
|
+
return extractExpressionPrefixLiteralInternal(context, variable.identifiers[0].parent.init);
|
|
80
90
|
}
|
|
81
91
|
function extractTemplateLiteralPrefixLiteral(context, expression) {
|
|
82
92
|
const literalParts = [...expression.expressions, ...expression.quasis].sort((a, b) => a.range[0] < b.range[0] ? -1 : 1);
|
|
@@ -88,12 +98,12 @@ function extractTemplateLiteralPrefixLiteral(context, expression) {
|
|
|
88
98
|
}
|
|
89
99
|
return part.value.raw;
|
|
90
100
|
}
|
|
91
|
-
return
|
|
101
|
+
return extractExpressionPrefixLiteralInternal(context, part);
|
|
92
102
|
}
|
|
93
103
|
return null;
|
|
94
104
|
}
|
|
95
105
|
// Literal suffix extraction
|
|
96
|
-
|
|
106
|
+
function extractExpressionSuffixLiteralInternal(context, expression) {
|
|
97
107
|
switch (expression.type) {
|
|
98
108
|
case 'BinaryExpression':
|
|
99
109
|
return extractBinaryExpressionSuffixLiteral(context, expression);
|
|
@@ -110,17 +120,17 @@ export function extractExpressionSuffixLiteral(context, expression) {
|
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
122
|
function extractBinaryExpressionSuffixLiteral(context, expression) {
|
|
113
|
-
return
|
|
123
|
+
return extractExpressionSuffixLiteralInternal(context, expression.right);
|
|
114
124
|
}
|
|
115
125
|
function extractVariableSuffixLiteral(context, expression) {
|
|
116
|
-
const variable = findVariable(
|
|
126
|
+
const variable = context.findVariable(expression);
|
|
117
127
|
if (variable === null ||
|
|
118
128
|
variable.identifiers.length !== 1 ||
|
|
119
129
|
variable.identifiers[0].parent.type !== 'VariableDeclarator' ||
|
|
120
130
|
variable.identifiers[0].parent.init === null) {
|
|
121
131
|
return null;
|
|
122
132
|
}
|
|
123
|
-
return
|
|
133
|
+
return extractExpressionSuffixLiteralInternal(context, variable.identifiers[0].parent.init);
|
|
124
134
|
}
|
|
125
135
|
function extractTemplateLiteralSuffixLiteral(context, expression) {
|
|
126
136
|
const literalParts = [...expression.expressions, ...expression.quasis].sort((a, b) => a.range[0] < b.range[0] ? -1 : 1);
|
|
@@ -132,7 +142,7 @@ function extractTemplateLiteralSuffixLiteral(context, expression) {
|
|
|
132
142
|
}
|
|
133
143
|
return part.value.raw;
|
|
134
144
|
}
|
|
135
|
-
return
|
|
145
|
+
return extractExpressionSuffixLiteralInternal(context, part);
|
|
136
146
|
}
|
|
137
147
|
return null;
|
|
138
148
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-svelte",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.4",
|
|
4
4
|
"description": "ESLint plugin for Svelte using AST",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/sveltejs/eslint-plugin-svelte.git",
|
|
8
|
+
"directory": "packages/eslint-plugin-svelte"
|
|
9
|
+
},
|
|
6
10
|
"homepage": "https://sveltejs.github.io/eslint-plugin-svelte",
|
|
7
11
|
"author": "Yosuke Ota (https://github.com/ota-meshi)",
|
|
8
12
|
"funding": "https://github.com/sponsors/ota-meshi",
|
|
@@ -58,6 +62,8 @@
|
|
|
58
62
|
"@types/postcss-safe-parser": "^5.0.4",
|
|
59
63
|
"@types/semver": "^7.7.0",
|
|
60
64
|
"@types/stylus": "^0.48.43",
|
|
65
|
+
"@typescript-eslint/scope-manager": "^8.43.0",
|
|
66
|
+
"@typescript-eslint/types": "^8.43.0",
|
|
61
67
|
"acorn": "^8.15.0",
|
|
62
68
|
"assert": "^2.1.0",
|
|
63
69
|
"esbuild": "^0.25.9",
|
|
@@ -65,6 +71,7 @@
|
|
|
65
71
|
"eslint-typegen": "^2.3.0",
|
|
66
72
|
"eslint-visitor-keys": "^4.2.1",
|
|
67
73
|
"espree": "^10.4.0",
|
|
74
|
+
"jiti": "^2.5.1",
|
|
68
75
|
"less": "^4.4.1",
|
|
69
76
|
"mocha": "~11.7.2",
|
|
70
77
|
"postcss-nested": "^7.0.2",
|