-
-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
89c4b89
commit 766cd04
Showing
24 changed files
with
441 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
--- | ||
pageClass: "rule-details" | ||
sidebarDepth: 0 | ||
title: "svelte/valid-context-access" | ||
description: "context functions must be called during component initialization." | ||
--- | ||
|
||
# svelte/valid-context-access | ||
|
||
> context functions must be called during component initialization. | ||
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge> | ||
|
||
## :book: Rule Details | ||
|
||
This rule reports where context API is called except during component initialization. | ||
|
||
<ESLintCodeBlock> | ||
|
||
<!--eslint-skip--> | ||
|
||
```svelte | ||
<script> | ||
/* eslint svelte/valid-context-access: "error" */ | ||
import { setContext, onMount } from "svelte" | ||
/** ✓ GOOD */ | ||
setContext("answer", 42) | ||
;(() => { | ||
setContext("answer", 42) | ||
})() | ||
const init = () => { | ||
setContext("answer", 42) | ||
} | ||
init() | ||
/** ✗ BAD */ | ||
const update = () => { | ||
setContext("answer", 42) | ||
} | ||
onMount(() => { | ||
update() | ||
setContext("answer", 42) | ||
}) | ||
</script> | ||
``` | ||
|
||
</ESLintCodeBlock> | ||
|
||
## :wrench: Options | ||
|
||
Nothing. | ||
|
||
## :books: Further Reading | ||
|
||
- [Svelte - Docs > RUN TIME > svelte > setContext](https://svelte.dev/docs#run-time-svelte-setcontext) | ||
- [Svelte - Docs > RUN TIME > svelte > getContext](https://svelte.dev/docs#run-time-svelte-getContext) | ||
- [Svelte - Docs > RUN TIME > svelte > hasContext](https://svelte.dev/docs#run-time-svelte-hasContext) | ||
- [Svelte - Docs > RUN TIME > svelte > getAllContexts](https://svelte.dev/docs#run-time-svelte-getAllContexts) | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/valid-context-access.ts) | ||
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/valid-context-access.ts) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import type { TSESTree } from "@typescript-eslint/types" | ||
import { ReferenceTracker } from "@eslint-community/eslint-utils" | ||
import type { RuleContext } from "../../types" | ||
|
||
type ContextName = "setContext" | "getContext" | "hasContext" | "getAllContexts" | ||
|
||
/** Extract svelte's context API references */ | ||
export function* extractContextReferences( | ||
context: RuleContext, | ||
contextNames: ContextName[] = [ | ||
"setContext", | ||
"getContext", | ||
"hasContext", | ||
"getAllContexts", | ||
], | ||
): Generator<{ node: TSESTree.CallExpression; name: string }, void> { | ||
const referenceTracker = new ReferenceTracker( | ||
context.getSourceCode().scopeManager.globalScope!, | ||
) | ||
for (const { node, path } of referenceTracker.iterateEsmReferences({ | ||
svelte: { | ||
[ReferenceTracker.ESM]: true, | ||
setContext: { | ||
[ReferenceTracker.CALL]: contextNames.includes("setContext"), | ||
}, | ||
getContext: { | ||
[ReferenceTracker.CALL]: contextNames.includes("getContext"), | ||
}, | ||
hasContext: { | ||
[ReferenceTracker.CALL]: contextNames.includes("hasContext"), | ||
}, | ||
getAllContexts: { | ||
[ReferenceTracker.CALL]: contextNames.includes("getAllContexts"), | ||
}, | ||
}, | ||
})) { | ||
yield { | ||
node: node as TSESTree.CallExpression, | ||
name: path[path.length - 1], | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { createRule } from "../utils" | ||
import { extractContextReferences } from "./reference-helpers/svelte-context" | ||
import type { TSESTree } from "@typescript-eslint/types" | ||
|
||
export default createRule("valid-context-access", { | ||
meta: { | ||
docs: { | ||
description: | ||
"context functions must be called during component initialization.", | ||
category: "Possible Errors", | ||
// TODO Switch to recommended in the major version. | ||
recommended: false, | ||
}, | ||
schema: [], | ||
messages: { | ||
unexpected: | ||
"Do not call {{function}} except during component initialization.", | ||
}, | ||
type: "problem", | ||
}, | ||
create(context) { | ||
const scopeManager = context.getSourceCode().scopeManager | ||
const toplevelScope = | ||
scopeManager.globalScope?.childScopes.find( | ||
(scope) => scope.type === "module", | ||
) || scopeManager.globalScope | ||
|
||
/** report ESLint error */ | ||
function report(node: TSESTree.CallExpression) { | ||
context.report({ | ||
loc: node.loc, | ||
messageId: "unexpected", | ||
data: { | ||
function: | ||
node.callee.type === "Identifier" | ||
? node.callee.name | ||
: "context function", | ||
}, | ||
}) | ||
} | ||
|
||
/** Get nodes where the variable is used */ | ||
function getReferences(id: TSESTree.Identifier | TSESTree.BindingName) { | ||
const variable = toplevelScope?.variables.find((v) => { | ||
if (id.type === "Identifier") { | ||
return v.identifiers.includes(id) | ||
} | ||
return false | ||
}) | ||
if (variable) { | ||
return variable.references.filter((r) => r.identifier !== id) | ||
} | ||
return [] | ||
} | ||
|
||
/** Let's lint! */ | ||
function doLint( | ||
visitedCallExpressions: TSESTree.CallExpression[], | ||
contextCallExpression: TSESTree.CallExpression, | ||
currentNode: TSESTree.CallExpression, | ||
) { | ||
let { parent } = currentNode | ||
if (parent?.type !== "ExpressionStatement") { | ||
report(contextCallExpression) | ||
return | ||
} | ||
while (parent) { | ||
parent = parent.parent | ||
if ( | ||
parent?.type === "VariableDeclaration" || | ||
parent?.type === "FunctionDeclaration" | ||
) { | ||
const references = | ||
parent.type === "VariableDeclaration" | ||
? getReferences(parent.declarations[0].id) | ||
: parent.id | ||
? getReferences(parent.id) | ||
: [] | ||
|
||
for (const reference of references) { | ||
if (reference.identifier?.parent?.type === "CallExpression") { | ||
if ( | ||
!visitedCallExpressions.includes(reference.identifier.parent) | ||
) { | ||
visitedCallExpressions.push(reference.identifier.parent) | ||
doLint( | ||
visitedCallExpressions, | ||
contextCallExpression, | ||
reference.identifier?.parent, | ||
) | ||
} | ||
} | ||
} | ||
} else if (parent?.type === "ExpressionStatement") { | ||
if (parent.expression.type !== "CallExpression") { | ||
report(contextCallExpression) | ||
} else if (parent.expression.callee.type === "Identifier") { | ||
report(contextCallExpression) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return { | ||
Program() { | ||
for (const { node } of extractContextReferences(context)) { | ||
const visitedCallExpressions: TSESTree.CallExpression[] = [] | ||
doLint(visitedCallExpressions, node, node) | ||
} | ||
}, | ||
} | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
tests/fixtures/rules/valid-context-access/invalid/case01-errors.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
- message: Do not call setContext except during component initialization. | ||
line: 11 | ||
column: 5 | ||
- message: Do not call getContext except during component initialization. | ||
line: 12 | ||
column: 5 | ||
- message: Do not call hasContext except during component initialization. | ||
line: 13 | ||
column: 5 | ||
- message: Do not call getAllContexts except during component initialization. | ||
line: 14 | ||
column: 5 | ||
- message: Do not call setContext except during component initialization. | ||
line: 18 | ||
column: 5 | ||
- message: Do not call getContext except during component initialization. | ||
line: 19 | ||
column: 5 | ||
- message: Do not call hasContext except during component initialization. | ||
line: 20 | ||
column: 5 | ||
- message: Do not call getAllContexts except during component initialization. | ||
line: 21 | ||
column: 5 | ||
- message: Do not call setContext except during component initialization. | ||
line: 25 | ||
column: 5 | ||
- message: Do not call getContext except during component initialization. | ||
line: 26 | ||
column: 5 | ||
- message: Do not call hasContext except during component initialization. | ||
line: 27 | ||
column: 5 | ||
- message: Do not call getAllContexts except during component initialization. | ||
line: 28 | ||
column: 5 |
36 changes: 36 additions & 0 deletions
36
tests/fixtures/rules/valid-context-access/invalid/case01-input.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<script> | ||
import { | ||
setContext, | ||
getContext, | ||
hasContext, | ||
getAllContexts, | ||
onMount, | ||
} from "svelte" | ||
const update1 = () => { | ||
setContext("answer", 42) | ||
getContext("answer") | ||
hasContext("answer") | ||
getAllContexts() | ||
} | ||
const update2 = function () { | ||
setContext("answer", 42) | ||
getContext("answer") | ||
hasContext("answer") | ||
getAllContexts() | ||
} | ||
function update3() { | ||
setContext("answer", 42) | ||
getContext("answer") | ||
hasContext("answer") | ||
getAllContexts() | ||
} | ||
onMount(() => { | ||
update1() | ||
update2() | ||
update3() | ||
}) | ||
</script> |
3 changes: 3 additions & 0 deletions
3
tests/fixtures/rules/valid-context-access/invalid/case02-errors.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
- message: Do not call setContext except during component initialization. | ||
line: 5 | ||
column: 5 |
7 changes: 7 additions & 0 deletions
7
tests/fixtures/rules/valid-context-access/invalid/case02-input.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<script> | ||
import { setContext, onMount } from "svelte" | ||
onMount(() => { | ||
setContext("answer", 42) | ||
}) | ||
</script> |
4 changes: 4 additions & 0 deletions
4
tests/fixtures/rules/valid-context-access/invalid/case03-errors.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
- message: Do not call setContext except during component initialization. | ||
line: 4 | ||
column: 5 | ||
suggestions: null |
10 changes: 10 additions & 0 deletions
10
tests/fixtures/rules/valid-context-access/invalid/case03-input.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<script> | ||
import { setContext, onMount } from "svelte" | ||
const something = () => { | ||
setContext("answer", 42) | ||
} | ||
something() | ||
</script> | ||
|
||
<button on:click={() => something()}>Click Me</button> |
4 changes: 4 additions & 0 deletions
4
tests/fixtures/rules/valid-context-access/invalid/case04-errors.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
- message: Do not call setContext except during component initialization. | ||
line: 4 | ||
column: 5 | ||
suggestions: null |
13 changes: 13 additions & 0 deletions
13
tests/fixtures/rules/valid-context-access/invalid/case04-input.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<script> | ||
import { setContext, onMount } from "svelte" | ||
const something = () => { | ||
setContext("answer", 42) | ||
} | ||
something() | ||
</script> | ||
|
||
{#if true} | ||
{@const foo = something()} | ||
<button>Click Me</button> | ||
{/if} |
4 changes: 4 additions & 0 deletions
4
tests/fixtures/rules/valid-context-access/invalid/case05-errors.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
- message: Do not call setContext except during component initialization. | ||
line: 4 | ||
column: 5 | ||
suggestions: null |
12 changes: 12 additions & 0 deletions
12
tests/fixtures/rules/valid-context-access/invalid/case05-input.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<script> | ||
import { setContext, onMount } from "svelte" | ||
const something = () => { | ||
setContext("answer", 42) | ||
} | ||
something() | ||
</script> | ||
|
||
{#if something()} | ||
<button>Click Me</button> | ||
{/if} |
Oops, something went wrong.