Skip to content

Commit 736e5a1

Browse files
committed
support setTimeout and others
1 parent 0f50d57 commit 736e5a1

File tree

8 files changed

+69
-19
lines changed

8 files changed

+69
-19
lines changed

Diff for: src/rules/infinite-reactive-loop.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ export default createRule("infinite-reactive-loop", {
357357
const tickCallExpressions = Array.from(
358358
extractSvelteLifeCycleReferences(context, ["tick"]),
359359
)
360-
const taskReferences = extractTaskReferences(context)
360+
const taskReferences = Array.from(extractTaskReferences(context))
361361
const reactiveVariableReferences = getReactiveVariableReferences(context)
362362

363363
return {

Diff for: src/rules/reference-helpers/microtask.ts

+22-10
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,36 @@ import type { TSESTree } from "@typescript-eslint/types"
22
import { ReferenceTracker } from "@eslint-community/eslint-utils"
33
import type { RuleContext } from "../../types"
44

5+
type FunctionName = "setTimeout" | "setInterval" | "queueMicrotask"
6+
57
/**
68
* Get usage of `setTimeout`, `setInterval`, `queueMicrotask`
79
*/
8-
export function extractTaskReferences(
10+
export function* extractTaskReferences(
911
context: RuleContext,
10-
): { node: TSESTree.CallExpression; name: string }[] {
12+
functionNames: FunctionName[] = [
13+
"setTimeout",
14+
"setInterval",
15+
"queueMicrotask",
16+
],
17+
): Generator<{ node: TSESTree.CallExpression; name: string }, void> {
1118
const referenceTracker = new ReferenceTracker(
1219
context.getSourceCode().scopeManager.globalScope!,
1320
)
14-
const a = referenceTracker.iterateGlobalReferences({
15-
setTimeout: { [ReferenceTracker.CALL]: true },
16-
setInterval: { [ReferenceTracker.CALL]: true },
17-
queueMicrotask: { [ReferenceTracker.CALL]: true },
18-
})
19-
return Array.from(a).map(({ node, path }) => {
20-
return {
21+
for (const { node, path } of referenceTracker.iterateGlobalReferences({
22+
setTimeout: {
23+
[ReferenceTracker.CALL]: functionNames.includes("setTimeout"),
24+
},
25+
setInterval: {
26+
[ReferenceTracker.CALL]: functionNames.includes("setInterval"),
27+
},
28+
queueMicrotask: {
29+
[ReferenceTracker.CALL]: functionNames.includes("queueMicrotask"),
30+
},
31+
})) {
32+
yield {
2133
node: node as TSESTree.CallExpression,
2234
name: path[path.length - 1],
2335
}
24-
})
36+
}
2537
}

Diff for: src/rules/valid-context-access.ts

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createRule } from "../utils"
22
import { extractContextReferences } from "./reference-helpers/svelte-context"
33
import { extractSvelteLifeCycleReferences } from "./reference-helpers/svelte-lifecycle"
4+
import { extractTaskReferences } from "./reference-helpers/microtask"
45
import type { AST } from "svelte-eslint-parser"
56
import type { TSESTree } from "@typescript-eslint/types"
67

@@ -29,6 +30,7 @@ export default createRule("valid-context-access", {
2930
const lifeCycleReferences = Array.from(
3031
extractSvelteLifeCycleReferences(context),
3132
).map((r) => r.node)
33+
const taskReferences = Array.from(extractTaskReferences(context))
3234

3335
// Extract <script> blocks that is not module=context.
3436
const scriptNotModuleElements: AST.SvelteScriptElement[] =
@@ -80,8 +82,8 @@ export default createRule("valid-context-access", {
8082
return []
8183
}
8284

83-
/** Return true if tnodeA is inside of nodeB. */
84-
function isInsideOfNodeB(
85+
/** Return true if tnodeB is inside of nodeA. */
86+
function isInsideOf(
8587
nodeA: TSESTree.Node | AST.SvelteScriptElement,
8688
nodeB: TSESTree.Node,
8789
) {
@@ -93,7 +95,7 @@ export default createRule("valid-context-access", {
9395
/** Return true if the node is there inside of <script> block that is not module=context. */
9496
function isInsideOfSvelteScriptElement(node: TSESTree.Node) {
9597
for (const script of scriptNotModuleElements) {
96-
if (isInsideOfNodeB(script, node)) {
98+
if (isInsideOf(script, node)) {
9799
return true
98100
}
99101
}
@@ -111,14 +113,24 @@ export default createRule("valid-context-access", {
111113
function isAfterAwait(node: TSESTree.CallExpression) {
112114
for (const awaitExpression of awaitExpressions) {
113115
const { belongingFunction, node: awaitNode } = awaitExpression
114-
if (isInsideOfNodeB(node, belongingFunction)) {
116+
if (isInsideOf(node, belongingFunction)) {
115117
continue
116118
}
117119
return awaitNode.range[0] <= node.range[0]
118120
}
119121
return false
120122
}
121123

124+
/** Return true if node is inside of task function */
125+
function isInsideTaskReference(node: TSESTree.CallExpression) {
126+
for (const taskReference of taskReferences) {
127+
if (isInsideOf(taskReference.node, node)) {
128+
return true
129+
}
130+
}
131+
return false
132+
}
133+
122134
/** Let's lint! */
123135
function doLint(
124136
visitedCallExpressions: TSESTree.CallExpression[],
@@ -136,6 +148,11 @@ export default createRule("valid-context-access", {
136148
return
137149
}
138150

151+
if (isInsideTaskReference(currentNode)) {
152+
report(contextCallExpression)
153+
return
154+
}
155+
139156
let { parent } = currentNode
140157
while (parent) {
141158
parent = parent.parent
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
- message: Do not call setContext except during component initialization.
2-
line: 3
3-
column: 3
2+
line: 5
3+
column: 5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
<script context="module">
1+
<script>
22
import { setContext } from "svelte"
3-
setContext("answer", 42)
3+
4+
setTimeout(() => {
5+
setContext("answer", 42)
6+
})
47
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- message: Do not call setContext except during component initialization.
2+
line: 6
3+
column: 7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import { setContext } from "svelte"
3+
4+
const doSomething = () => {
5+
setTimeout(() => {
6+
setContext("answer", 42)
7+
})
8+
}
9+
10+
doSomething()
11+
</script>

Diff for: tests/src/rules/valid-context-access.ts

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ const tester = new RuleTester({
77
ecmaVersion: 2020,
88
sourceType: "module",
99
},
10+
env: {
11+
browser: true,
12+
es2017: true,
13+
},
1014
})
1115

1216
tester.run(

0 commit comments

Comments
 (0)