eslint-plugin-svelte 2.26.0 → 2.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Introduction
2
2
 
3
- `eslint-plugin-svelte` is [ESLint] plugin for [Svelte].
3
+ `eslint-plugin-svelte` is the official [ESLint] plugin for [Svelte].
4
4
  It provides many unique check rules by using the template AST.
5
5
  You can check on the [Online DEMO](https://sveltejs.github.io/eslint-plugin-svelte/playground/).
6
6
 
@@ -23,12 +23,6 @@ You can check on the [Online DEMO](https://sveltejs.github.io/eslint-plugin-svel
23
23
  [ESLint] plugin for [Svelte].
24
24
  It provides many unique check rules using the AST generated by [svelte-eslint-parser].
25
25
 
26
- ### ❓ Why?
27
-
28
- [Svelte] has the official [ESLint] plugin the [eslint-plugin-svelte3]. The [eslint-plugin-svelte3] works well enough to check scripts. However, it does not handle the AST of the template, which makes it very difficult for third parties to create their own the [ESLint] rules for the [Svelte].
29
-
30
- The [svelte-eslint-parser] aims to make it easy to create your own rules for the [Svelte] by allowing the template AST to be used in the rules.
31
-
32
26
  ### ❗ Attention
33
27
 
34
28
  The [svelte-eslint-parser] and the `eslint-plugin-svelte` can not be used with the [eslint-plugin-svelte3].
@@ -319,6 +313,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
319
313
  | [svelte/no-export-load-in-svelte-module-in-kit-pages](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-export-load-in-svelte-module-in-kit-pages/) | disallow exporting load functions in `*.svelte` module in Svelte Kit page components. | |
320
314
  | [svelte/no-not-function-handler](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-not-function-handler/) | disallow use of not function in event handler | :star: |
321
315
  | [svelte/no-object-in-text-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-object-in-text-mustaches/) | disallow objects in text mustache interpolation | :star: |
316
+ | [svelte/no-reactive-reassign](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-reassign/) | disallow reassigning reactive values | |
322
317
  | [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: |
323
318
  | [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 | |
324
319
  | [svelte/no-unknown-style-directive-property](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: |
@@ -345,6 +340,7 @@ These rules relate to better ways of doing things to help you avoid problems:
345
340
  | [svelte/block-lang](https://sveltejs.github.io/eslint-plugin-svelte/rules/block-lang/) | disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks. | |
346
341
  | [svelte/button-has-type](https://sveltejs.github.io/eslint-plugin-svelte/rules/button-has-type/) | disallow usage of button without an explicit type attribute | |
347
342
  | [svelte/no-at-debug-tags](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star: |
343
+ | [svelte/no-immutable-reactive-statements](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-immutable-reactive-statements/) | disallow reactive statements that don't reference reactive values. | |
348
344
  | [svelte/no-reactive-functions](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-functions/) | it's not necessary to define functions in reactive statements | :bulb: |
349
345
  | [svelte/no-reactive-literals](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-literals/) | don't assign literal values in reactive statements | :bulb: |
350
346
  | [svelte/no-unused-svelte-ignore](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../types").RuleModule;
2
+ export default _default;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../utils");
4
+ exports.default = (0, utils_1.createRule)("no-immutable-reactive-statements", {
5
+ meta: {
6
+ docs: {
7
+ description: "disallow reactive statements that don't reference reactive values.",
8
+ category: "Best Practices",
9
+ recommended: false,
10
+ },
11
+ schema: [],
12
+ messages: {
13
+ immutable: "This statement is not reactive because all variables referenced in the reactive statement are immutable.",
14
+ },
15
+ type: "suggestion",
16
+ },
17
+ create(context) {
18
+ const scopeManager = context.getSourceCode().scopeManager;
19
+ const globalScope = scopeManager.globalScope;
20
+ const toplevelScope = globalScope?.childScopes.find((scope) => scope.type === "module") ||
21
+ globalScope;
22
+ if (!globalScope || !toplevelScope) {
23
+ return {};
24
+ }
25
+ const cacheMutableVariable = new WeakMap();
26
+ function isMutableVariableReference(reference) {
27
+ if (reference.identifier.name.startsWith("$")) {
28
+ return true;
29
+ }
30
+ if (!reference.resolved) {
31
+ return true;
32
+ }
33
+ return isMutableVariable(reference.resolved);
34
+ }
35
+ function isMutableVariable(variable) {
36
+ const cache = cacheMutableVariable.get(variable);
37
+ if (cache != null) {
38
+ return cache;
39
+ }
40
+ if (variable.defs.length === 0) {
41
+ return true;
42
+ }
43
+ const isMutable = variable.defs.some((def) => {
44
+ if (def.type === "Variable") {
45
+ const parent = def.parent;
46
+ if (parent.kind === "const") {
47
+ return false;
48
+ }
49
+ const pp = parent.parent;
50
+ if (pp &&
51
+ pp.type === "ExportNamedDeclaration" &&
52
+ pp.declaration === parent) {
53
+ return true;
54
+ }
55
+ return hasWrite(variable);
56
+ }
57
+ if (def.type === "ImportBinding") {
58
+ return false;
59
+ }
60
+ if (def.node.type === "AssignmentExpression") {
61
+ return true;
62
+ }
63
+ return false;
64
+ });
65
+ cacheMutableVariable.set(variable, isMutable);
66
+ return isMutable;
67
+ }
68
+ function hasWrite(variable) {
69
+ const defIds = variable.defs.map((def) => def.name);
70
+ return variable.references.some((reference) => reference.isWrite() &&
71
+ !defIds.some((defId) => defId.range[0] <= reference.identifier.range[0] &&
72
+ reference.identifier.range[1] <= defId.range[1]));
73
+ }
74
+ function* iterateRangeReferences(scope, range) {
75
+ for (const variable of scope.variables) {
76
+ for (const reference of variable.references) {
77
+ if (range[0] <= reference.identifier.range[0] &&
78
+ reference.identifier.range[1] <= range[1]) {
79
+ yield reference;
80
+ }
81
+ }
82
+ }
83
+ }
84
+ return {
85
+ SvelteReactiveStatement(node) {
86
+ for (const reference of iterateRangeReferences(toplevelScope, node.range)) {
87
+ if (reference.isWriteOnly()) {
88
+ continue;
89
+ }
90
+ if (isMutableVariableReference(reference)) {
91
+ return;
92
+ }
93
+ }
94
+ if (globalScope.through.some((reference) => node.range[0] <= reference.identifier.range[0] &&
95
+ reference.identifier.range[1] <= node.range[1])) {
96
+ return;
97
+ }
98
+ context.report({
99
+ node: node.body.type === "ExpressionStatement" &&
100
+ node.body.expression.type === "AssignmentExpression" &&
101
+ node.body.expression.operator === "="
102
+ ? node.body.expression.right
103
+ : node.body,
104
+ messageId: "immutable",
105
+ });
106
+ },
107
+ };
108
+ },
109
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../types").RuleModule;
2
+ export default _default;
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../utils");
4
+ const eslint_utils_1 = require("@eslint-community/eslint-utils");
5
+ exports.default = (0, utils_1.createRule)("no-reactive-reassign", {
6
+ meta: {
7
+ docs: {
8
+ description: "disallow reassigning reactive values",
9
+ category: "Possible Errors",
10
+ recommended: false,
11
+ },
12
+ schema: [
13
+ {
14
+ type: "object",
15
+ properties: {
16
+ props: {
17
+ type: "boolean",
18
+ },
19
+ },
20
+ additionalProperties: false,
21
+ },
22
+ ],
23
+ messages: {
24
+ assignmentToReactiveValue: "Assignment to reactive value '{{name}}'.",
25
+ assignmentToReactiveValueProp: "Assignment to property of reactive value '{{name}}'.",
26
+ },
27
+ type: "problem",
28
+ },
29
+ create(context) {
30
+ const props = context.options[0]?.props !== false;
31
+ const sourceCode = context.getSourceCode();
32
+ const scopeManager = sourceCode.scopeManager;
33
+ const globalScope = scopeManager.globalScope;
34
+ const toplevelScope = globalScope?.childScopes.find((scope) => scope.type === "module") ||
35
+ globalScope;
36
+ if (!globalScope || !toplevelScope) {
37
+ return {};
38
+ }
39
+ const CHECK_REASSIGN = {
40
+ UpdateExpression: ({ parent }) => ({ type: "reassign", node: parent }),
41
+ UnaryExpression: ({ parent }) => {
42
+ if (parent.operator === "delete") {
43
+ return { type: "reassign", node: parent };
44
+ }
45
+ return null;
46
+ },
47
+ AssignmentExpression: ({ node, parent, }) => {
48
+ if (parent.left === node) {
49
+ return { type: "reassign", node: parent };
50
+ }
51
+ return null;
52
+ },
53
+ ForInStatement: ({ node, parent, }) => {
54
+ if (parent.left === node) {
55
+ return { type: "reassign", node: parent };
56
+ }
57
+ return null;
58
+ },
59
+ ForOfStatement: ({ node, parent, }) => {
60
+ if (parent.left === node) {
61
+ return { type: "reassign", node: parent };
62
+ }
63
+ return null;
64
+ },
65
+ CallExpression: ({ node, parent, pathNodes, }) => {
66
+ if (pathNodes.length > 0 && parent.callee === node) {
67
+ const mem = pathNodes[pathNodes.length - 1];
68
+ const callName = (0, eslint_utils_1.getPropertyName)(mem);
69
+ if (callName &&
70
+ /^(?:push|pop|shift|unshift|reverse|splice|sort|copyWithin|fill)$/u.test(callName)) {
71
+ return {
72
+ type: "reassign",
73
+ node: parent,
74
+ pathNodes: pathNodes.slice(0, -1),
75
+ };
76
+ }
77
+ }
78
+ return null;
79
+ },
80
+ MemberExpression: ({ node, parent, pathNodes, }) => {
81
+ if (parent.object === node) {
82
+ return {
83
+ type: "check",
84
+ node: parent,
85
+ pathNodes: [...pathNodes, parent],
86
+ };
87
+ }
88
+ return null;
89
+ },
90
+ ChainExpression: ({ parent }) => {
91
+ return { type: "check", node: parent };
92
+ },
93
+ ConditionalExpression: ({ node, parent, }) => {
94
+ if (parent.test === node) {
95
+ return null;
96
+ }
97
+ return { type: "check", node: parent };
98
+ },
99
+ Property: ({ node, parent }) => {
100
+ if (parent.value === node &&
101
+ parent.parent &&
102
+ parent.parent.type === "ObjectPattern") {
103
+ return { type: "check", node: parent.parent };
104
+ }
105
+ return null;
106
+ },
107
+ ArrayPattern: ({ node, parent }) => {
108
+ if (parent.elements.includes(node)) {
109
+ return { type: "check", node: parent };
110
+ }
111
+ return null;
112
+ },
113
+ RestElement: ({ node, parent }) => {
114
+ if (parent.argument === node && parent.parent) {
115
+ return {
116
+ type: "check",
117
+ node: parent.parent,
118
+ };
119
+ }
120
+ return null;
121
+ },
122
+ SvelteDirective: ({ node, parent, }) => {
123
+ if (parent.kind !== "Binding") {
124
+ return null;
125
+ }
126
+ if (parent.shorthand || parent.expression === node) {
127
+ return {
128
+ type: "reassign",
129
+ node: parent,
130
+ };
131
+ }
132
+ return null;
133
+ },
134
+ };
135
+ function getReassignData(expr) {
136
+ let pathNodes = [];
137
+ let node = expr;
138
+ let parent;
139
+ while ((parent = node.parent)) {
140
+ const check = CHECK_REASSIGN[parent.type];
141
+ if (!check) {
142
+ return null;
143
+ }
144
+ const result = check({ node, parent, pathNodes });
145
+ if (!result) {
146
+ return null;
147
+ }
148
+ pathNodes = result.pathNodes || pathNodes;
149
+ if (result.type === "reassign") {
150
+ return {
151
+ node: result.node,
152
+ pathNodes,
153
+ };
154
+ }
155
+ node = result.node;
156
+ }
157
+ return null;
158
+ }
159
+ return {
160
+ SvelteReactiveStatement(node) {
161
+ if (node.body.type !== "ExpressionStatement" ||
162
+ node.body.expression.type !== "AssignmentExpression" ||
163
+ node.body.expression.operator !== "=") {
164
+ return;
165
+ }
166
+ const assignment = node.body.expression;
167
+ for (const variable of toplevelScope.variables) {
168
+ if (!variable.defs.some((def) => def.node === assignment)) {
169
+ continue;
170
+ }
171
+ for (const reference of variable.references) {
172
+ const id = reference.identifier;
173
+ if ((assignment.left.range[0] <= id.range[0] &&
174
+ id.range[1] <= assignment.left.range[1]) ||
175
+ id.type === "JSXIdentifier") {
176
+ continue;
177
+ }
178
+ const reassign = getReassignData(id);
179
+ if (!reassign) {
180
+ continue;
181
+ }
182
+ if (!props && reassign.pathNodes.length > 0)
183
+ continue;
184
+ context.report({
185
+ node: reassign.node,
186
+ messageId: reassign.pathNodes.length === 0
187
+ ? "assignmentToReactiveValue"
188
+ : "assignmentToReactiveValueProp",
189
+ data: {
190
+ name: id.name,
191
+ },
192
+ });
193
+ }
194
+ }
195
+ },
196
+ };
197
+ },
198
+ });
@@ -29,11 +29,13 @@ const no_dupe_use_directives_1 = __importDefault(require("../rules/no-dupe-use-d
29
29
  const no_dynamic_slot_name_1 = __importDefault(require("../rules/no-dynamic-slot-name"));
30
30
  const no_export_load_in_svelte_module_in_kit_pages_1 = __importDefault(require("../rules/no-export-load-in-svelte-module-in-kit-pages"));
31
31
  const no_extra_reactive_curlies_1 = __importDefault(require("../rules/no-extra-reactive-curlies"));
32
+ const no_immutable_reactive_statements_1 = __importDefault(require("../rules/no-immutable-reactive-statements"));
32
33
  const no_inner_declarations_1 = __importDefault(require("../rules/no-inner-declarations"));
33
34
  const no_not_function_handler_1 = __importDefault(require("../rules/no-not-function-handler"));
34
35
  const no_object_in_text_mustaches_1 = __importDefault(require("../rules/no-object-in-text-mustaches"));
35
36
  const no_reactive_functions_1 = __importDefault(require("../rules/no-reactive-functions"));
36
37
  const no_reactive_literals_1 = __importDefault(require("../rules/no-reactive-literals"));
38
+ const no_reactive_reassign_1 = __importDefault(require("../rules/no-reactive-reassign"));
37
39
  const no_shorthand_style_property_overrides_1 = __importDefault(require("../rules/no-shorthand-style-property-overrides"));
38
40
  const no_spaces_around_equal_signs_in_attribute_1 = __importDefault(require("../rules/no-spaces-around-equal-signs-in-attribute"));
39
41
  const no_store_async_1 = __importDefault(require("../rules/no-store-async"));
@@ -83,11 +85,13 @@ exports.rules = [
83
85
  no_dynamic_slot_name_1.default,
84
86
  no_export_load_in_svelte_module_in_kit_pages_1.default,
85
87
  no_extra_reactive_curlies_1.default,
88
+ no_immutable_reactive_statements_1.default,
86
89
  no_inner_declarations_1.default,
87
90
  no_not_function_handler_1.default,
88
91
  no_object_in_text_mustaches_1.default,
89
92
  no_reactive_functions_1.default,
90
93
  no_reactive_literals_1.default,
94
+ no_reactive_reassign_1.default,
91
95
  no_shorthand_style_property_overrides_1.default,
92
96
  no_spaces_around_equal_signs_in_attribute_1.default,
93
97
  no_store_async_1.default,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-svelte",
3
- "version": "2.26.0",
3
+ "version": "2.27.1",
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",
@@ -73,7 +73,7 @@
73
73
  "postcss": "^8.4.5",
74
74
  "postcss-load-config": "^3.1.4",
75
75
  "postcss-safe-parser": "^6.0.0",
76
- "svelte-eslint-parser": "^0.26.0"
76
+ "svelte-eslint-parser": "^0.27.0"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@1stg/browserslist-config": "^1.2.3",
@@ -81,7 +81,7 @@
81
81
  "@1stg/lint-staged": "^3.3.0",
82
82
  "@1stg/remark-config": "^4.0.3",
83
83
  "@1stg/simple-git-hooks": "^0.2.1",
84
- "@1stg/stylelint-config": "^4.6.1",
84
+ "@1stg/stylelint-config": "^5.0.0",
85
85
  "@babel/core": "^7.16.0",
86
86
  "@babel/eslint-parser": "^7.17.0",
87
87
  "@babel/plugin-proposal-function-bind": "^7.16.7",
@@ -158,7 +158,7 @@
158
158
  "semver": "^7.3.5",
159
159
  "simple-git-hooks": "^2.8.0",
160
160
  "stylelint": "^15.0.0",
161
- "stylelint-config-standard": "^32.0.0",
161
+ "stylelint-config-standard": "^33.0.0",
162
162
  "stylus": "^0.59.0",
163
163
  "svelte": "^3.46.1",
164
164
  "svelte-adapter-ghpages": "0.1.0",