eslint-plugin-svelte 2.11.0 → 2.13.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.
Files changed (31) hide show
  1. package/README.md +46 -6
  2. package/lib/rules/html-self-closing.js +12 -6
  3. package/lib/rules/indent-helpers/ast.d.ts +3 -3
  4. package/lib/rules/indent-helpers/es.js +7 -9
  5. package/lib/rules/no-dom-manipulating.d.ts +2 -0
  6. package/lib/rules/no-dom-manipulating.js +109 -0
  7. package/lib/rules/no-export-load-in-svelte-module-in-kit-pages.d.ts +2 -0
  8. package/lib/rules/no-export-load-in-svelte-module-in-kit-pages.js +41 -0
  9. package/lib/rules/prefer-destructured-store-props.js +4 -2
  10. package/lib/rules/reference-helpers/svelte-store.d.ts +6 -2
  11. package/lib/rules/reference-helpers/svelte-store.js +99 -1
  12. package/lib/rules/require-store-callbacks-use-set-param.d.ts +2 -0
  13. package/lib/rules/require-store-callbacks-use-set-param.js +43 -0
  14. package/lib/rules/require-store-reactive-access.d.ts +2 -0
  15. package/lib/rules/require-store-reactive-access.js +205 -0
  16. package/lib/rules/valid-prop-names-in-kit-pages.d.ts +2 -0
  17. package/lib/rules/valid-prop-names-in-kit-pages.js +61 -0
  18. package/lib/types-for-node.d.ts +72 -73
  19. package/lib/types.d.ts +12 -6
  20. package/lib/utils/ast-utils.d.ts +6 -6
  21. package/lib/utils/ast-utils.js +11 -9
  22. package/lib/utils/cache.d.ts +4 -0
  23. package/lib/utils/cache.js +32 -0
  24. package/lib/utils/css-utils/style-attribute.d.ts +4 -4
  25. package/lib/utils/eslint-core.d.ts +3 -3
  26. package/lib/utils/get-package-json.d.ts +5 -0
  27. package/lib/utils/get-package-json.js +55 -0
  28. package/lib/utils/rules.js +10 -0
  29. package/lib/utils/svelte-kit.d.ts +2 -0
  30. package/lib/utils/svelte-kit.js +48 -0
  31. package/package.json +8 -8
package/README.md CHANGED
@@ -188,12 +188,6 @@ See also <https://github.com/ota-meshi/svelte-eslint-parser#readme>.
188
188
 
189
189
  You can change the behavior of this plugin with some settings.
190
190
 
191
- - `ignoreWarnings` (optional) ... Specifies an array of rules that ignore reports in the template.
192
- For example, set rules on the template that cannot avoid false positives.
193
- - `compileOptions` (optional) ... Specifies options for Svelte compile. Effects rules that use Svelte compile. The target rules are [svelte/valid-compile](https://ota-meshi.github.io/eslint-plugin-svelte/rules/valid-compile/) and [svelte/no-unused-svelte-ignore](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/). **Note that it has no effect on ESLint's custom parser**.
194
- - `postcss` (optional) ... Specifies options related to PostCSS. You can disable the PostCSS process by specifying `false`.
195
- - `configFilePath` (optional) ... Specifies the path of the directory containing the PostCSS configuration.
196
-
197
191
  e.g.
198
192
 
199
193
  ```js
@@ -210,6 +204,47 @@ module.exports = {
210
204
  configFilePath: "./path/to/my/postcss.config.js",
211
205
  },
212
206
  },
207
+ kit: {
208
+ files: {
209
+ routes: "src/routes",
210
+ },
211
+ },
212
+ },
213
+ },
214
+ // ...
215
+ }
216
+ ```
217
+
218
+ #### settings.svelte.ignoreWarnings
219
+
220
+ Specifies an array of rules that ignore reports in the template.
221
+ For example, set rules on the template that cannot avoid false positives.
222
+
223
+ #### settings.svelte.compileOptions
224
+
225
+ Specifies options for Svelte compile. Effects rules that use Svelte compile. The target rules are [svelte/valid-compile](https://ota-meshi.github.io/eslint-plugin-svelte/rules/valid-compile/) and [svelte/no-unused-svelte-ignore](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/). **Note that it has no effect on ESLint's custom parser**.
226
+
227
+ - `postcss` ... Specifies options related to PostCSS. You can disable the PostCSS process by specifying `false`.
228
+ - `configFilePath` ... Specifies the path of the directory containing the PostCSS configuration.
229
+
230
+ #### settings.svelte.kit
231
+
232
+ If you use SvelteKit with not default configuration, you need to set below configurations.
233
+ The schema is subset of SvelteKit's configuration.
234
+ Therefore please check [SvelteKit docs](https://kit.svelte.dev/docs/configuration) for more details.
235
+
236
+ e.g.
237
+
238
+ ```js
239
+ module.exports = {
240
+ // ...
241
+ settings: {
242
+ svelte: {
243
+ kit: {
244
+ files: {
245
+ routes: "src/routes",
246
+ },
247
+ },
213
248
  },
214
249
  },
215
250
  // ...
@@ -263,15 +298,20 @@ These rules relate to possible syntax or logic errors in Svelte code:
263
298
 
264
299
  | Rule ID | Description | |
265
300
  |:--------|:------------|:---|
301
+ | [svelte/no-dom-manipulating](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dom-manipulating/) | disallow DOM manipulating | |
266
302
  | [svelte/no-dupe-else-if-blocks](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dupe-else-if-blocks/) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: |
267
303
  | [svelte/no-dupe-style-properties](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dupe-style-properties/) | disallow duplicate style properties | :star: |
268
304
  | [svelte/no-dynamic-slot-name](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dynamic-slot-name/) | disallow dynamic slot name | :star::wrench: |
305
+ | [svelte/no-export-load-in-svelte-module-in-kit-pages](https://ota-meshi.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. | |
269
306
  | [svelte/no-not-function-handler](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-not-function-handler/) | disallow use of not function in event handler | :star: |
270
307
  | [svelte/no-object-in-text-mustaches](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-object-in-text-mustaches/) | disallow objects in text mustache interpolation | :star: |
271
308
  | [svelte/no-shorthand-style-property-overrides](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-shorthand-style-property-overrides/) | disallow shorthand style properties that override related longhand properties | :star: |
272
309
  | [svelte/no-store-async](https://ota-meshi.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 | |
273
310
  | [svelte/no-unknown-style-directive-property](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: |
311
+ | [svelte/require-store-callbacks-use-set-param](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param | |
312
+ | [svelte/require-store-reactive-access](https://ota-meshi.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. | :wrench: |
274
313
  | [svelte/valid-compile](https://ota-meshi.github.io/eslint-plugin-svelte/rules/valid-compile/) | disallow warnings when compiling. | :star: |
314
+ | [svelte/valid-prop-names-in-kit-pages](https://ota-meshi.github.io/eslint-plugin-svelte/rules/valid-prop-names-in-kit-pages/) | disallow props other than data or errors in Svelte Kit page components. | |
275
315
 
276
316
  ## Security Vulnerability
277
317
 
@@ -49,14 +49,14 @@ exports.default = (0, utils_1.createRule)("html-self-closing", {
49
49
  },
50
50
  ],
51
51
  },
52
- create(ctx) {
52
+ create(context) {
53
53
  let options = {
54
54
  void: "always",
55
55
  normal: "always",
56
56
  component: "always",
57
57
  svelte: "always",
58
58
  };
59
- const option = ctx.options?.[0];
59
+ const option = context.options?.[0];
60
60
  switch (option) {
61
61
  case "none":
62
62
  options = {
@@ -103,16 +103,22 @@ exports.default = (0, utils_1.createRule)("html-self-closing", {
103
103
  }
104
104
  return true;
105
105
  }
106
- function report(node, close) {
106
+ function report(node, shouldBeClosed) {
107
107
  const elementType = getElementType(node);
108
- ctx.report({
108
+ context.report({
109
109
  node,
110
- messageId: close ? "requireClosing" : "disallowClosing",
110
+ loc: {
111
+ start: context
112
+ .getSourceCode()
113
+ .getLocFromIndex(node.startTag.range[1] - (node.startTag.selfClosing ? 2 : 1)),
114
+ end: node.loc.end,
115
+ },
116
+ messageId: shouldBeClosed ? "requireClosing" : "disallowClosing",
111
117
  data: {
112
118
  type: TYPE_MESSAGES[elementType],
113
119
  },
114
120
  *fix(fixer) {
115
- if (close) {
121
+ if (shouldBeClosed) {
116
122
  for (const child of node.children) {
117
123
  yield fixer.removeRange(child.range);
118
124
  }
@@ -1,6 +1,6 @@
1
1
  import type { AST } from "svelte-eslint-parser";
2
- import type * as ESTree from "estree";
2
+ import type { TSESTree } from "@typescript-eslint/types";
3
3
  declare type AnyToken = AST.Token | AST.Comment;
4
- export declare function isWhitespace(token: AnyToken | ESTree.Comment | null | undefined): boolean;
5
- export declare function isNotWhitespace(token: AnyToken | ESTree.Comment | null | undefined): boolean;
4
+ export declare function isWhitespace(token: AnyToken | TSESTree.Comment | null | undefined): boolean;
5
+ export declare function isNotWhitespace(token: AnyToken | TSESTree.Comment | null | undefined): boolean;
6
6
  export {};
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defineVisitor = void 0;
4
4
  const commons_1 = require("./commons");
5
5
  const eslint_utils_1 = require("eslint-utils");
6
+ const ast_utils_1 = require("../../utils/ast-utils");
6
7
  function defineVisitor(context) {
7
8
  const { sourceCode, offsets, options } = context;
8
9
  function getRootLeft(node) {
9
10
  let target = node;
10
- let parent = getParent(target);
11
+ let parent = (0, ast_utils_1.getParent)(target);
11
12
  while (parent &&
12
13
  (parent.type === "AssignmentExpression" ||
13
14
  parent.type === "AssignmentPattern" ||
@@ -18,7 +19,7 @@ function defineVisitor(context) {
18
19
  break;
19
20
  }
20
21
  target = parent;
21
- parent = getParent(target);
22
+ parent = (0, ast_utils_1.getParent)(target);
22
23
  }
23
24
  return target.left;
24
25
  }
@@ -161,12 +162,12 @@ function defineVisitor(context) {
161
162
  });
162
163
  const alternateToken = sourceCode.getTokenAfter(colonToken);
163
164
  let baseNode = node;
164
- let parent = getParent(baseNode);
165
+ let parent = (0, ast_utils_1.getParent)(baseNode);
165
166
  while (parent &&
166
167
  parent.type === "ConditionalExpression" &&
167
168
  parent.alternate === baseNode) {
168
169
  baseNode = parent;
169
- parent = getParent(baseNode);
170
+ parent = (0, ast_utils_1.getParent)(baseNode);
170
171
  }
171
172
  const baseToken = sourceCode.getFirstToken(baseNode);
172
173
  offsets.setOffsetToken([questionToken, colonToken], 1, baseToken);
@@ -335,9 +336,9 @@ function defineVisitor(context) {
335
336
  filter: eslint_utils_1.isOpeningParenToken,
336
337
  includeComments: false,
337
338
  });
338
- let bodyBaseToken;
339
+ let bodyBaseToken = null;
339
340
  if (firstToken.type === "Punctuator") {
340
- bodyBaseToken = sourceCode.getFirstToken(getParent(node));
341
+ bodyBaseToken = sourceCode.getFirstToken((0, ast_utils_1.getParent)(node));
341
342
  }
342
343
  else {
343
344
  let tokenOffset = 0;
@@ -735,9 +736,6 @@ function defineVisitor(context) {
735
736
  };
736
737
  }
737
738
  exports.defineVisitor = defineVisitor;
738
- function getParent(node) {
739
- return node.parent || null;
740
- }
741
739
  function isOptionalToken(token) {
742
740
  return token.type === "Punctuator" && token.value === "?.";
743
741
  }
@@ -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
+ const ast_utils_1 = require("../utils/ast-utils");
5
+ const eslint_utils_1 = require("eslint-utils");
6
+ const DOM_MANIPULATING_METHODS = new Set([
7
+ "appendChild",
8
+ "insertBefore",
9
+ "normalize",
10
+ "removeChild",
11
+ "replaceChild",
12
+ "after",
13
+ "append",
14
+ "before",
15
+ "insertAdjacentElement",
16
+ "insertAdjacentHTML",
17
+ "insertAdjacentText",
18
+ "prepend",
19
+ "remove",
20
+ "replaceChildren",
21
+ "replaceWith",
22
+ ]);
23
+ const DOM_MANIPULATING_PROPERTIES = new Set([
24
+ "textContent",
25
+ "innerHTML",
26
+ "outerHTML",
27
+ "innerText",
28
+ "outerText",
29
+ ]);
30
+ exports.default = (0, utils_1.createRule)("no-dom-manipulating", {
31
+ meta: {
32
+ docs: {
33
+ description: "disallow DOM manipulating",
34
+ category: "Possible Errors",
35
+ recommended: false,
36
+ },
37
+ schema: [],
38
+ messages: {
39
+ disallowManipulateDOM: "Don't manipulate the DOM directly. The Svelte runtime can get confused if there is a difference between the actual DOM and the DOM expected by the Svelte runtime.",
40
+ },
41
+ type: "problem",
42
+ },
43
+ create(context) {
44
+ const domVariables = new Set();
45
+ function verifyIdentifier(node) {
46
+ const member = node.parent;
47
+ if (member?.type !== "MemberExpression" || member.object !== node) {
48
+ return;
49
+ }
50
+ const name = (0, eslint_utils_1.getPropertyName)(member);
51
+ if (!name) {
52
+ return;
53
+ }
54
+ let target = member;
55
+ let parent = target.parent;
56
+ while (parent?.type === "ChainExpression") {
57
+ target = parent;
58
+ parent = parent.parent;
59
+ }
60
+ if (!parent) {
61
+ return;
62
+ }
63
+ if (parent.type === "CallExpression") {
64
+ if (parent.callee !== target || !DOM_MANIPULATING_METHODS.has(name)) {
65
+ return;
66
+ }
67
+ }
68
+ else if (parent.type === "AssignmentExpression") {
69
+ if (parent.left !== target || !DOM_MANIPULATING_PROPERTIES.has(name)) {
70
+ return;
71
+ }
72
+ }
73
+ context.report({
74
+ node: member,
75
+ messageId: "disallowManipulateDOM",
76
+ });
77
+ }
78
+ return {
79
+ "SvelteDirective[kind='Binding']"(node) {
80
+ if (node.key.name.name !== "this" ||
81
+ !node.expression ||
82
+ node.expression.type !== "Identifier") {
83
+ return;
84
+ }
85
+ const element = node.parent.parent;
86
+ if (element.type !== "SvelteElement" || !isHTMLElement(element)) {
87
+ return;
88
+ }
89
+ const variable = (0, ast_utils_1.findVariable)(context, node.expression);
90
+ if (!variable ||
91
+ (variable.scope.type !== "module" && variable.scope.type !== "global")) {
92
+ return;
93
+ }
94
+ domVariables.add(variable);
95
+ },
96
+ "Program:exit"() {
97
+ for (const variable of domVariables) {
98
+ for (const reference of variable.references) {
99
+ verifyIdentifier(reference.identifier);
100
+ }
101
+ }
102
+ },
103
+ };
104
+ function isHTMLElement(node) {
105
+ return (node.kind === "html" ||
106
+ (node.kind === "special" && (0, ast_utils_1.getNodeName)(node) === "svelte:element"));
107
+ }
108
+ },
109
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../types").RuleModule;
2
+ export default _default;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../utils");
4
+ const svelte_kit_1 = require("../utils/svelte-kit");
5
+ exports.default = (0, utils_1.createRule)("no-export-load-in-svelte-module-in-kit-pages", {
6
+ meta: {
7
+ docs: {
8
+ description: "disallow exporting load functions in `*.svelte` module in Svelte Kit page components.",
9
+ category: "Possible Errors",
10
+ recommended: false,
11
+ },
12
+ schema: [],
13
+ messages: {
14
+ unexpected: "disallow exporting load functions in `*.svelte` module in Svelte Kit page components.",
15
+ },
16
+ type: "problem",
17
+ },
18
+ create(context) {
19
+ if (!(0, svelte_kit_1.isKitPageComponent)(context)) {
20
+ return {};
21
+ }
22
+ let isModule = false;
23
+ return {
24
+ [`Program > SvelteScriptElement > SvelteStartTag > SvelteAttribute[key.name="context"] > SvelteLiteral[value="module"]`]: () => {
25
+ isModule = true;
26
+ },
27
+ "Program > SvelteScriptElement:exit": () => {
28
+ isModule = false;
29
+ },
30
+ [`:matches(ExportNamedDeclaration > FunctionDeclaration, ExportNamedDeclaration > VariableDeclaration > VariableDeclarator) > Identifier.id[name="load"]`]: (node) => {
31
+ if (!isModule)
32
+ return {};
33
+ return context.report({
34
+ node,
35
+ loc: node.loc,
36
+ messageId: "unexpected",
37
+ });
38
+ },
39
+ };
40
+ },
41
+ });
@@ -47,7 +47,8 @@ exports.default = (0, utils_1.createRule)("prefer-destructured-store-props", {
47
47
  }
48
48
  }
49
49
  function isReactiveVariableDefinitionWithMemberExpression(node) {
50
- return (node.parent?.type === "MemberExpression" &&
50
+ return (node.type === "Identifier" &&
51
+ node.parent?.type === "MemberExpression" &&
51
52
  node.parent.object === node &&
52
53
  (0, eslint_utils_1.getPropertyName)(node.parent) === propName &&
53
54
  node.parent.parent?.type === "AssignmentExpression" &&
@@ -58,7 +59,8 @@ exports.default = (0, utils_1.createRule)("prefer-destructured-store-props", {
58
59
  .parent?.type === "SvelteReactiveStatement");
59
60
  }
60
61
  function isReactiveVariableDefinitionWithDestructuring(node) {
61
- return (node.parent?.type === "AssignmentExpression" &&
62
+ return (node.type === "Identifier" &&
63
+ node.parent?.type === "AssignmentExpression" &&
62
64
  node.parent.right === node &&
63
65
  node.parent.left.type === "ObjectPattern" &&
64
66
  node.parent.parent?.type === "ExpressionStatement" &&
@@ -1,8 +1,12 @@
1
- import type * as ESTree from "estree";
1
+ import type { TSESTree } from "@typescript-eslint/types";
2
2
  import type { RuleContext } from "../../types";
3
3
  declare type StoreName = "writable" | "readable" | "derived";
4
4
  export declare function extractStoreReferences(context: RuleContext, storeNames?: StoreName[]): Generator<{
5
- node: ESTree.CallExpression;
5
+ node: TSESTree.CallExpression;
6
6
  name: string;
7
7
  }, void>;
8
+ export declare type StoreChecker = (node: TSESTree.Expression, options?: {
9
+ consistent?: boolean;
10
+ }) => boolean;
11
+ export declare function createStoreChecker(context: RuleContext): StoreChecker;
8
12
  export {};
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractStoreReferences = void 0;
3
+ exports.createStoreChecker = exports.extractStoreReferences = void 0;
4
4
  const eslint_utils_1 = require("eslint-utils");
5
+ const ts_utils_1 = require("../../utils/ts-utils");
6
+ const ast_utils_1 = require("../../utils/ast-utils");
5
7
  function* extractStoreReferences(context, storeNames = ["writable", "readable", "derived"]) {
6
8
  const referenceTracker = new eslint_utils_1.ReferenceTracker(context.getScope());
7
9
  for (const { node, path } of referenceTracker.iterateEsmReferences({
@@ -25,3 +27,99 @@ function* extractStoreReferences(context, storeNames = ["writable", "readable",
25
27
  }
26
28
  }
27
29
  exports.extractStoreReferences = extractStoreReferences;
30
+ function createStoreChecker(context) {
31
+ const tools = (0, ts_utils_1.getTypeScriptTools)(context);
32
+ const checker = tools
33
+ ? createStoreCheckerForTS(tools)
34
+ : createStoreCheckerForES(context);
35
+ return (node, options) => checker(node, {
36
+ consistent: options?.consistent ?? false,
37
+ });
38
+ }
39
+ exports.createStoreChecker = createStoreChecker;
40
+ function createStoreCheckerForES(context) {
41
+ const storeVariables = new Map();
42
+ for (const { node } of extractStoreReferences(context)) {
43
+ const parent = (0, ast_utils_1.getParent)(node);
44
+ if (!parent ||
45
+ parent.type !== "VariableDeclarator" ||
46
+ parent.id.type !== "Identifier") {
47
+ continue;
48
+ }
49
+ const decl = (0, ast_utils_1.getParent)(parent);
50
+ if (!decl || decl.type !== "VariableDeclaration") {
51
+ continue;
52
+ }
53
+ const variable = (0, ast_utils_1.findVariable)(context, parent.id);
54
+ if (variable) {
55
+ storeVariables.set(variable, { const: decl.kind === "const" });
56
+ }
57
+ }
58
+ return (node, options) => {
59
+ if (node.type !== "Identifier" || node.name.startsWith("$")) {
60
+ return false;
61
+ }
62
+ const variable = (0, ast_utils_1.findVariable)(context, node);
63
+ if (!variable) {
64
+ return false;
65
+ }
66
+ const info = storeVariables.get(variable);
67
+ if (!info) {
68
+ return false;
69
+ }
70
+ return options.consistent ? info.const : true;
71
+ };
72
+ }
73
+ function createStoreCheckerForTS(tools) {
74
+ const { service } = tools;
75
+ const checker = service.program.getTypeChecker();
76
+ const tsNodeMap = service.esTreeNodeToTSNodeMap;
77
+ return (node, options) => {
78
+ const tsNode = tsNodeMap.get(node);
79
+ if (!tsNode) {
80
+ return false;
81
+ }
82
+ const type = checker.getTypeAtLocation(tsNode);
83
+ return isStoreType(checker.getApparentType(type));
84
+ function isStoreType(type) {
85
+ return eachTypeCheck(type, options, (type) => {
86
+ const subscribe = type.getProperty("subscribe");
87
+ if (!subscribe) {
88
+ return false;
89
+ }
90
+ const subscribeType = checker.getTypeOfSymbolAtLocation(subscribe, tsNode);
91
+ return isStoreSubscribeSignatureType(subscribeType);
92
+ });
93
+ }
94
+ function isStoreSubscribeSignatureType(type) {
95
+ return eachTypeCheck(type, options, (type) => {
96
+ for (const signature of type.getCallSignatures()) {
97
+ if (signature.parameters.length >= 2 &&
98
+ maybeFunctionSymbol(signature.parameters[0]) &&
99
+ maybeFunctionSymbol(signature.parameters[1])) {
100
+ return true;
101
+ }
102
+ }
103
+ return false;
104
+ });
105
+ }
106
+ function maybeFunctionSymbol(param) {
107
+ const type = checker.getApparentType(checker.getTypeOfSymbolAtLocation(param, tsNode));
108
+ return maybeFunctionType(type);
109
+ }
110
+ function maybeFunctionType(type) {
111
+ return eachTypeCheck(type, { consistent: false }, (type) => {
112
+ return type.getCallSignatures().length > 0;
113
+ });
114
+ }
115
+ };
116
+ }
117
+ function eachTypeCheck(type, options, check) {
118
+ if (type.isUnion()) {
119
+ if (options.consistent) {
120
+ return type.types.every((t) => eachTypeCheck(t, options, check));
121
+ }
122
+ return type.types.some((t) => eachTypeCheck(t, options, check));
123
+ }
124
+ return check(type);
125
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../types").RuleModule;
2
+ export default _default;
@@ -0,0 +1,43 @@
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)("require-store-callbacks-use-set-param", {
6
+ meta: {
7
+ docs: {
8
+ description: "store callbacks must use `set` param",
9
+ category: "Possible Errors",
10
+ recommended: false,
11
+ },
12
+ schema: [],
13
+ messages: {
14
+ unexpected: "Store callbacks must use `set` param.",
15
+ },
16
+ type: "suggestion",
17
+ },
18
+ create(context) {
19
+ return {
20
+ Program() {
21
+ for (const { node } of (0, svelte_store_1.extractStoreReferences)(context, [
22
+ "readable",
23
+ "writable",
24
+ ])) {
25
+ const [_, fn] = node.arguments;
26
+ if (!fn ||
27
+ (fn.type !== "ArrowFunctionExpression" &&
28
+ fn.type !== "FunctionExpression")) {
29
+ continue;
30
+ }
31
+ const param = fn.params[0];
32
+ if (!param || (param.type === "Identifier" && param.name !== "set")) {
33
+ context.report({
34
+ node: fn,
35
+ loc: fn.loc,
36
+ messageId: "unexpected",
37
+ });
38
+ }
39
+ }
40
+ },
41
+ };
42
+ },
43
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("../types").RuleModule;
2
+ export default _default;