eslint-plugin-svelte 3.6.0 → 3.8.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 +1 -0
- package/lib/main.d.ts +1 -1
- package/lib/meta.d.ts +1 -1
- package/lib/meta.js +1 -1
- package/lib/rule-types.d.ts +5 -0
- package/lib/rules/no-top-level-browser-globals.d.ts +2 -0
- package/lib/rules/no-top-level-browser-globals.js +350 -0
- package/lib/types-for-node.d.ts +4 -0
- package/lib/utils/ast-utils.d.ts +2 -2
- package/lib/utils/ast-utils.js +2 -0
- package/lib/utils/rules.js +2 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -271,6 +271,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
|
|
|
271
271
|
| [svelte/no-reactive-reassign](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-reassign/) | disallow reassigning reactive values | :star: |
|
|
272
272
|
| [svelte/no-shorthand-style-property-overrides](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-shorthand-style-property-overrides/) | disallow shorthand style properties that override related longhand properties | :star: |
|
|
273
273
|
| [svelte/no-store-async](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-store-async/) | disallow using async/await inside svelte stores because it causes issues with the auto-unsubscribing features | :star: |
|
|
274
|
+
| [svelte/no-top-level-browser-globals](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-top-level-browser-globals/) | disallow using top-level browser global variables | |
|
|
274
275
|
| [svelte/no-unknown-style-directive-property](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: |
|
|
275
276
|
| [svelte/require-store-callbacks-use-set-param](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param | :bulb: |
|
|
276
277
|
| [svelte/require-store-reactive-access](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-reactive-access/) | disallow to use of the store itself as an operand. Need to use $ prefix or get function. | :star::wrench: |
|
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.
|
|
17
|
+
version: "3.8.0";
|
|
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.
|
|
2
|
+
export declare const version: "3.8.0";
|
package/lib/meta.js
CHANGED
package/lib/rule-types.d.ts
CHANGED
|
@@ -244,6 +244,11 @@ export interface RuleOptions {
|
|
|
244
244
|
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-target-blank/
|
|
245
245
|
*/
|
|
246
246
|
'svelte/no-target-blank'?: Linter.RuleEntry<SvelteNoTargetBlank>;
|
|
247
|
+
/**
|
|
248
|
+
* disallow using top-level browser global variables
|
|
249
|
+
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-top-level-browser-globals/
|
|
250
|
+
*/
|
|
251
|
+
'svelte/no-top-level-browser-globals'?: Linter.RuleEntry<[]>;
|
|
247
252
|
/**
|
|
248
253
|
* disallow trailing whitespace at the end of lines
|
|
249
254
|
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-trailing-spaces/
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { ReferenceTracker, getStaticValue } from '@eslint-community/eslint-utils';
|
|
2
|
+
import { createRule } from '../utils/index.js';
|
|
3
|
+
import globals from 'globals';
|
|
4
|
+
import { findVariable, getScope } from '../utils/ast-utils.js';
|
|
5
|
+
export default createRule('no-top-level-browser-globals', {
|
|
6
|
+
meta: {
|
|
7
|
+
docs: {
|
|
8
|
+
description: 'disallow using top-level browser global variables',
|
|
9
|
+
category: 'Possible Errors',
|
|
10
|
+
recommended: false
|
|
11
|
+
},
|
|
12
|
+
schema: [],
|
|
13
|
+
messages: {
|
|
14
|
+
unexpectedGlobal: 'Unexpected top-level browser global variable "{{name}}".'
|
|
15
|
+
},
|
|
16
|
+
type: 'problem',
|
|
17
|
+
conditions: [{ svelteFileTypes: ['.svelte', '.svelte.[js|ts]'] }]
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
const sourceCode = context.sourceCode;
|
|
21
|
+
const blowerGlobals = getBrowserGlobals();
|
|
22
|
+
const referenceTracker = new ReferenceTracker(sourceCode.scopeManager.globalScope, {
|
|
23
|
+
// Specifies the global variables that are allowed to prevent `window.window` from being iterated over.
|
|
24
|
+
globalObjectNames: ['globalThis']
|
|
25
|
+
});
|
|
26
|
+
const maybeGuards = [];
|
|
27
|
+
const functions = [];
|
|
28
|
+
function enterFunction(node) {
|
|
29
|
+
if (isTopLevelLocation(node)) {
|
|
30
|
+
functions.push(node);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function enterMetaProperty(node) {
|
|
34
|
+
if (node.meta.name !== 'import' || node.property.name !== 'meta')
|
|
35
|
+
return;
|
|
36
|
+
for (const ref of referenceTracker.iteratePropertyReferences(node, {
|
|
37
|
+
env: {
|
|
38
|
+
// See https://vite.dev/guide/ssr#conditional-logic
|
|
39
|
+
SSR: {
|
|
40
|
+
[ReferenceTracker.READ]: true
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})) {
|
|
44
|
+
if (ref.node.type === 'Identifier' || ref.node.type === 'MemberExpression') {
|
|
45
|
+
const guardChecker = getGuardChecker({ node: ref.node, not: true });
|
|
46
|
+
if (guardChecker) {
|
|
47
|
+
maybeGuards.push({
|
|
48
|
+
isAvailableLocation: guardChecker,
|
|
49
|
+
browserEnvironment: true
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function verifyGlobalReferences() {
|
|
56
|
+
// Collects guarded location checkers by checking module references
|
|
57
|
+
// that can check the browser environment.
|
|
58
|
+
for (const referenceNode of iterateBrowserCheckerModuleReferences()) {
|
|
59
|
+
if (!isTopLevelLocation(referenceNode))
|
|
60
|
+
continue;
|
|
61
|
+
const guardChecker = getGuardChecker({ node: referenceNode });
|
|
62
|
+
if (guardChecker) {
|
|
63
|
+
maybeGuards.push({
|
|
64
|
+
isAvailableLocation: guardChecker,
|
|
65
|
+
browserEnvironment: true
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const reportCandidates = [];
|
|
70
|
+
// Collects references to global variables.
|
|
71
|
+
for (const ref of iterateBrowserGlobalReferences()) {
|
|
72
|
+
if (!isTopLevelLocation(ref.node))
|
|
73
|
+
continue;
|
|
74
|
+
const guardChecker = getGuardCheckerFromReference(ref.node);
|
|
75
|
+
if (guardChecker) {
|
|
76
|
+
const name = ref.path.join('.');
|
|
77
|
+
maybeGuards.push({
|
|
78
|
+
reference: { node: ref.node, name },
|
|
79
|
+
isAvailableLocation: guardChecker,
|
|
80
|
+
browserEnvironment: name === 'window' || name === 'document'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
reportCandidates.push(ref);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
for (const ref of reportCandidates) {
|
|
88
|
+
const name = ref.path.join('.');
|
|
89
|
+
if (isAvailableLocation({ node: ref.node, name })) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
context.report({
|
|
93
|
+
node: ref.node,
|
|
94
|
+
messageId: 'unexpectedGlobal',
|
|
95
|
+
data: { name }
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
':function': enterFunction,
|
|
101
|
+
MetaProperty: enterMetaProperty,
|
|
102
|
+
'Program:exit': verifyGlobalReferences
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Checks whether the node is in a location where the expression is available or not.
|
|
106
|
+
* @returns `true` if the expression is available.
|
|
107
|
+
*/
|
|
108
|
+
function isAvailableLocation(ref) {
|
|
109
|
+
for (const guard of maybeGuards.reverse()) {
|
|
110
|
+
if (guard.isAvailableLocation(ref.node)) {
|
|
111
|
+
if (guard.browserEnvironment || guard.reference?.name === ref.name) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Checks whether the node is in a top-level location.
|
|
120
|
+
* @returns `true` if the node is in a top-level location.
|
|
121
|
+
*/
|
|
122
|
+
function isTopLevelLocation(node) {
|
|
123
|
+
for (const func of functions) {
|
|
124
|
+
if (func.range[0] <= node.range[0] && node.range[1] <= func.range[1]) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Iterate over the references of modules that can check the browser environment.
|
|
132
|
+
*/
|
|
133
|
+
function* iterateBrowserCheckerModuleReferences() {
|
|
134
|
+
for (const ref of referenceTracker.iterateEsmReferences({
|
|
135
|
+
'esm-env': {
|
|
136
|
+
[ReferenceTracker.ESM]: true,
|
|
137
|
+
// See https://www.npmjs.com/package/esm-env
|
|
138
|
+
BROWSER: {
|
|
139
|
+
[ReferenceTracker.READ]: true
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
'$app/environment': {
|
|
143
|
+
[ReferenceTracker.ESM]: true,
|
|
144
|
+
// See https://svelte.dev/docs/kit/$app-environment#browser
|
|
145
|
+
browser: {
|
|
146
|
+
[ReferenceTracker.READ]: true
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
})) {
|
|
150
|
+
if (ref.node.type === 'Identifier' || ref.node.type === 'MemberExpression') {
|
|
151
|
+
yield ref.node;
|
|
152
|
+
}
|
|
153
|
+
else if (ref.node.type === 'ImportSpecifier') {
|
|
154
|
+
const variable = findVariable(context, ref.node.local);
|
|
155
|
+
if (variable) {
|
|
156
|
+
for (const reference of variable.references) {
|
|
157
|
+
if (reference.isRead() && reference.identifier.type === 'Identifier') {
|
|
158
|
+
yield reference.identifier;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Iterate over the used references of global variables.
|
|
167
|
+
*/
|
|
168
|
+
function* iterateBrowserGlobalReferences() {
|
|
169
|
+
yield* referenceTracker.iterateGlobalReferences(Object.fromEntries(blowerGlobals.map((name) => [
|
|
170
|
+
name,
|
|
171
|
+
{
|
|
172
|
+
[ReferenceTracker.READ]: true
|
|
173
|
+
}
|
|
174
|
+
])));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* If the node is a reference used in a guard clause that checks if the node is in a browser environment,
|
|
178
|
+
* it returns information about the expression that checks if the browser variable is available.
|
|
179
|
+
* @returns The guard info.
|
|
180
|
+
*/
|
|
181
|
+
function getGuardCheckerFromReference(node) {
|
|
182
|
+
const parent = node.parent;
|
|
183
|
+
if (!parent)
|
|
184
|
+
return null;
|
|
185
|
+
if (parent.type === 'BinaryExpression') {
|
|
186
|
+
if (parent.operator === 'instanceof' &&
|
|
187
|
+
parent.left === node &&
|
|
188
|
+
node.type === 'MemberExpression') {
|
|
189
|
+
// e.g. if (globalThis.window instanceof X)
|
|
190
|
+
return getGuardChecker({ node: parent });
|
|
191
|
+
}
|
|
192
|
+
const operand = parent.left === node ? parent.right : parent.right === node ? parent.left : null;
|
|
193
|
+
if (!operand)
|
|
194
|
+
return null;
|
|
195
|
+
const staticValue = getStaticValue(operand, getScope(context, operand));
|
|
196
|
+
if (!staticValue)
|
|
197
|
+
return null;
|
|
198
|
+
if (staticValue.value === undefined && node.type === 'MemberExpression') {
|
|
199
|
+
if (parent.operator === '!==' || parent.operator === '!=') {
|
|
200
|
+
// e.g. if (globalThis.window !== undefined), if (globalThis.window != undefined)
|
|
201
|
+
return getGuardChecker({ node: parent });
|
|
202
|
+
}
|
|
203
|
+
else if (parent.operator === '===' || parent.operator === '==') {
|
|
204
|
+
// e.g. if (globalThis.window === undefined), if (globalThis.window == undefined)
|
|
205
|
+
return getGuardChecker({ node: parent, not: true });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (staticValue.value === null && node.type === 'MemberExpression') {
|
|
209
|
+
if (parent.operator === '!=') {
|
|
210
|
+
// e.g. if (globalThis.window != null)
|
|
211
|
+
return getGuardChecker({ node: parent });
|
|
212
|
+
}
|
|
213
|
+
else if (parent.operator === '==') {
|
|
214
|
+
// e.g. if (globalThis.window == null)
|
|
215
|
+
return getGuardChecker({ node: parent, not: true });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
if (parent.type === 'UnaryExpression' &&
|
|
221
|
+
parent.operator === 'typeof' &&
|
|
222
|
+
parent.argument === node) {
|
|
223
|
+
const pp = parent.parent;
|
|
224
|
+
if (!pp || pp.type !== 'BinaryExpression') {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const staticValue = getStaticValue(pp.left === parent ? pp.right : pp.left, getScope(context, node));
|
|
228
|
+
if (!staticValue)
|
|
229
|
+
return null;
|
|
230
|
+
if (staticValue.value !== 'undefined' && staticValue.value !== 'object') {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
if (pp.operator === '!==' || pp.operator === '!=') {
|
|
234
|
+
if (staticValue.value === 'undefined') {
|
|
235
|
+
// e.g. if (typeof window !== "undefined"), if (typeof window != "undefined")
|
|
236
|
+
return getGuardChecker({ node: pp });
|
|
237
|
+
}
|
|
238
|
+
// e.g. if (typeof window !== "object"), if (typeof window != "object")
|
|
239
|
+
return getGuardChecker({ node: pp, not: true });
|
|
240
|
+
}
|
|
241
|
+
else if (pp.operator === '===' || pp.operator === '==') {
|
|
242
|
+
if (staticValue.value === 'undefined') {
|
|
243
|
+
// e.g. if (typeof window === "undefined"), if (typeof window == "undefined")
|
|
244
|
+
return getGuardChecker({ node: pp, not: true });
|
|
245
|
+
}
|
|
246
|
+
// e.g. if (typeof window === "object"), if (typeof window == "object")
|
|
247
|
+
return getGuardChecker({ node: pp });
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
if (node.type === 'MemberExpression') {
|
|
252
|
+
if (((parent.type === 'CallExpression' && parent.callee === node) ||
|
|
253
|
+
(parent.type === 'MemberExpression' && parent.object === node)) &&
|
|
254
|
+
parent.optional) {
|
|
255
|
+
// e.g. globalThis.location?.href
|
|
256
|
+
return (n) => n === node;
|
|
257
|
+
}
|
|
258
|
+
// e.g. if (globalThis.window)
|
|
259
|
+
return getGuardChecker({ node });
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* If the node is a guard clause checking,
|
|
265
|
+
* returns a function to check if the node is available.
|
|
266
|
+
*/
|
|
267
|
+
function getGuardChecker(guardInfo) {
|
|
268
|
+
const parent = guardInfo.node.parent;
|
|
269
|
+
if (!parent)
|
|
270
|
+
return null;
|
|
271
|
+
if (parent.type === 'ConditionalExpression') {
|
|
272
|
+
const block = guardInfo.not ? parent.alternate : parent.consequent;
|
|
273
|
+
return (n) => block.range[0] <= n.range[0] && n.range[1] <= block.range[1];
|
|
274
|
+
}
|
|
275
|
+
if (parent.type === 'UnaryExpression' && parent.operator === '!') {
|
|
276
|
+
return getGuardChecker({ not: !guardInfo.not, node: parent });
|
|
277
|
+
}
|
|
278
|
+
if (parent.type === 'IfStatement' && parent.test === guardInfo.node) {
|
|
279
|
+
if (!guardInfo.not) {
|
|
280
|
+
const block = parent.consequent;
|
|
281
|
+
return (n) => block.range[0] <= n.range[0] && n.range[1] <= block.range[1];
|
|
282
|
+
}
|
|
283
|
+
if (parent.alternate) {
|
|
284
|
+
const block = parent.alternate;
|
|
285
|
+
return (n) => block.range[0] <= n.range[0] && n.range[1] <= block.range[1];
|
|
286
|
+
}
|
|
287
|
+
if (!hasJumpStatementInAllPath(parent.consequent)) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
const pp = parent.parent;
|
|
291
|
+
if (!pp || (pp.type !== 'BlockStatement' && pp.type !== 'Program')) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const start = parent.range[1];
|
|
295
|
+
const end = pp.range[1];
|
|
296
|
+
return (n) => start <= n.range[0] && n.range[1] <= end;
|
|
297
|
+
}
|
|
298
|
+
if (!guardInfo.not &&
|
|
299
|
+
parent.type === 'LogicalExpression' &&
|
|
300
|
+
parent.operator === '&&' &&
|
|
301
|
+
parent.left === guardInfo.node) {
|
|
302
|
+
const block = parent.right;
|
|
303
|
+
return (n) => block.range[0] <= n.range[0] && n.range[1] <= block.range[1];
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
/**
|
|
310
|
+
* Get the list of browser-specific globals.
|
|
311
|
+
*/
|
|
312
|
+
function getBrowserGlobals() {
|
|
313
|
+
const nodeGlobals = new Set(Object.keys(globals.node));
|
|
314
|
+
return [
|
|
315
|
+
'window',
|
|
316
|
+
'document',
|
|
317
|
+
...Object.keys(globals.browser).filter((name) => !nodeGlobals.has(name))
|
|
318
|
+
];
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Checks whether all paths of a given statement have jump statements.
|
|
322
|
+
* @param {Statement} statement
|
|
323
|
+
* @returns {boolean}
|
|
324
|
+
*/
|
|
325
|
+
function hasJumpStatementInAllPath(statement) {
|
|
326
|
+
if (isJumpStatement(statement)) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
if (statement.type === 'BlockStatement') {
|
|
330
|
+
return statement.body.some(hasJumpStatementInAllPath);
|
|
331
|
+
}
|
|
332
|
+
if (statement.type === 'IfStatement') {
|
|
333
|
+
if (!statement.alternate) {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
return (hasJumpStatementInAllPath(statement.alternate) &&
|
|
337
|
+
hasJumpStatementInAllPath(statement.consequent));
|
|
338
|
+
}
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Checks whether the given statement is a jump statement.
|
|
343
|
+
* @param {Statement} statement
|
|
344
|
+
* @returns {statement is JumpStatement}
|
|
345
|
+
*/
|
|
346
|
+
function isJumpStatement(statement) {
|
|
347
|
+
return (statement.type === 'ReturnStatement' ||
|
|
348
|
+
statement.type === 'ContinueStatement' ||
|
|
349
|
+
statement.type === 'BreakStatement');
|
|
350
|
+
}
|
package/lib/types-for-node.d.ts
CHANGED
|
@@ -395,6 +395,8 @@ export type ASTNodeListener = {
|
|
|
395
395
|
'SvelteShorthandAttribute:exit'?: (node: AST.SvelteShorthandAttribute & ASTNodeWithParent) => void;
|
|
396
396
|
SvelteSpreadAttribute?: (node: AST.SvelteSpreadAttribute & ASTNodeWithParent) => void;
|
|
397
397
|
'SvelteSpreadAttribute:exit'?: (node: AST.SvelteSpreadAttribute & ASTNodeWithParent) => void;
|
|
398
|
+
SvelteAttachTag?: (node: AST.SvelteAttachTag & ASTNodeWithParent) => void;
|
|
399
|
+
'SvelteAttachTag:exit'?: (node: AST.SvelteAttachTag & ASTNodeWithParent) => void;
|
|
398
400
|
SvelteDirective?: (node: AST.SvelteDirective & ASTNodeWithParent) => void;
|
|
399
401
|
'SvelteDirective:exit'?: (node: AST.SvelteDirective & ASTNodeWithParent) => void;
|
|
400
402
|
SvelteStyleDirective?: (node: AST.SvelteStyleDirective & ASTNodeWithParent) => void;
|
|
@@ -781,6 +783,8 @@ export type SvelteNodeListener = {
|
|
|
781
783
|
'SvelteShorthandAttribute:exit'?: (node: AST.SvelteShorthandAttribute & ASTNodeWithParent) => void;
|
|
782
784
|
SvelteSpreadAttribute?: (node: AST.SvelteSpreadAttribute & ASTNodeWithParent) => void;
|
|
783
785
|
'SvelteSpreadAttribute:exit'?: (node: AST.SvelteSpreadAttribute & ASTNodeWithParent) => void;
|
|
786
|
+
SvelteAttachTag?: (node: AST.SvelteAttachTag & ASTNodeWithParent) => void;
|
|
787
|
+
'SvelteAttachTag:exit'?: (node: AST.SvelteAttachTag & ASTNodeWithParent) => void;
|
|
784
788
|
SvelteDirective?: (node: AST.SvelteDirective & ASTNodeWithParent) => void;
|
|
785
789
|
'SvelteDirective:exit'?: (node: AST.SvelteDirective & ASTNodeWithParent) => void;
|
|
786
790
|
SvelteStyleDirective?: (node: AST.SvelteStyleDirective & ASTNodeWithParent) => void;
|
package/lib/utils/ast-utils.d.ts
CHANGED
|
@@ -89,13 +89,13 @@ export declare function getMustacheTokens(node: SvAST.SvelteDirective | SvAST.Sv
|
|
|
89
89
|
closeToken: SvAST.Token;
|
|
90
90
|
} | null;
|
|
91
91
|
/** Get attribute key text */
|
|
92
|
-
export declare function getAttributeKeyText(node: SvAST.SvelteAttribute | SvAST.SvelteShorthandAttribute | SvAST.SvelteStyleDirective | SvAST.SvelteDirective | SvAST.SvelteSpecialDirective | SvAST.SvelteGenericsDirective, context: RuleContext): string;
|
|
92
|
+
export declare function getAttributeKeyText(node: SvAST.SvelteAttribute | SvAST.SvelteShorthandAttribute | SvAST.SvelteStyleDirective | SvAST.SvelteDirective | SvAST.SvelteSpecialDirective | SvAST.SvelteGenericsDirective | SvAST.SvelteAttachTag, context: RuleContext): string;
|
|
93
93
|
/** Get directive name */
|
|
94
94
|
export declare function getDirectiveName(node: SvAST.SvelteDirective): string;
|
|
95
95
|
/**
|
|
96
96
|
* Extract all class names used in a HTML element attribute.
|
|
97
97
|
*/
|
|
98
|
-
export declare function findClassesInAttribute(attribute: SvAST.SvelteAttribute | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute | SvAST.SvelteDirective | SvAST.SvelteStyleDirective | SvAST.SvelteSpecialDirective | SvAST.SvelteGenericsDirective): string[];
|
|
98
|
+
export declare function findClassesInAttribute(attribute: SvAST.SvelteAttribute | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute | SvAST.SvelteDirective | SvAST.SvelteStyleDirective | SvAST.SvelteSpecialDirective | SvAST.SvelteGenericsDirective | SvAST.SvelteAttachTag): string[];
|
|
99
99
|
/**
|
|
100
100
|
* Returns name of SvelteElement
|
|
101
101
|
*/
|
package/lib/utils/ast-utils.js
CHANGED
|
@@ -331,6 +331,8 @@ export function getAttributeKeyText(node, context) {
|
|
|
331
331
|
const dir = getDirectiveName(node);
|
|
332
332
|
return `${dir}:${getSimpleNameFromNode(node.key.name, context)}${node.key.modifiers.length ? `|${node.key.modifiers.join('|')}` : ''}`;
|
|
333
333
|
}
|
|
334
|
+
case 'SvelteAttachTag':
|
|
335
|
+
return '@attach';
|
|
334
336
|
default:
|
|
335
337
|
throw new Error(`Unknown node type: ${
|
|
336
338
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- error
|
package/lib/utils/rules.js
CHANGED
|
@@ -45,6 +45,7 @@ import noSpacesAroundEqualSignsInAttribute from '../rules/no-spaces-around-equal
|
|
|
45
45
|
import noStoreAsync from '../rules/no-store-async.js';
|
|
46
46
|
import noSvelteInternal from '../rules/no-svelte-internal.js';
|
|
47
47
|
import noTargetBlank from '../rules/no-target-blank.js';
|
|
48
|
+
import noTopLevelBrowserGlobals from '../rules/no-top-level-browser-globals.js';
|
|
48
49
|
import noTrailingSpaces from '../rules/no-trailing-spaces.js';
|
|
49
50
|
import noUnknownStyleDirectiveProperty from '../rules/no-unknown-style-directive-property.js';
|
|
50
51
|
import noUnnecessaryStateWrap from '../rules/no-unnecessary-state-wrap.js';
|
|
@@ -122,6 +123,7 @@ export const rules = [
|
|
|
122
123
|
noStoreAsync,
|
|
123
124
|
noSvelteInternal,
|
|
124
125
|
noTargetBlank,
|
|
126
|
+
noTopLevelBrowserGlobals,
|
|
125
127
|
noTrailingSpaces,
|
|
126
128
|
noUnknownStyleDirectiveProperty,
|
|
127
129
|
noUnnecessaryStateWrap,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-svelte",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "ESLint plugin for Svelte using AST",
|
|
5
5
|
"repository": "git+https://github.com/sveltejs/eslint-plugin-svelte.git",
|
|
6
6
|
"homepage": "https://sveltejs.github.io/eslint-plugin-svelte",
|
|
@@ -32,15 +32,16 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@eslint-community/eslint-utils": "^4.
|
|
35
|
+
"@eslint-community/eslint-utils": "^4.6.1",
|
|
36
36
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
|
37
37
|
"esutils": "^2.0.3",
|
|
38
|
+
"globals": "^16.0.0",
|
|
38
39
|
"known-css-properties": "^0.36.0",
|
|
39
40
|
"postcss": "^8.4.49",
|
|
40
41
|
"postcss-load-config": "^3.1.4",
|
|
41
42
|
"postcss-safe-parser": "^7.0.0",
|
|
42
43
|
"semver": "^7.6.3",
|
|
43
|
-
"svelte-eslint-parser": "^1.
|
|
44
|
+
"svelte-eslint-parser": "^1.2.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@babel/core": "^7.26.0",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"sass": "^1.81.0",
|
|
72
73
|
"source-map-js": "^1.2.1",
|
|
73
74
|
"stylus": "^0.64.0",
|
|
74
|
-
"svelte": "^5.
|
|
75
|
+
"svelte": "^5.30.1",
|
|
75
76
|
"svelte-i18n": "^4.0.1",
|
|
76
77
|
"tsx": "^4.19.2",
|
|
77
78
|
"type-coverage": "^2.29.7",
|