-
-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(@typescript-eslint/no-shadow): ignore
{#snippet}
if it uses und…
…er component
- Loading branch information
1 parent
b97a13e
commit 5a495dc
Showing
15 changed files
with
282 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'eslint-plugin-svelte': patch | ||
--- | ||
|
||
fix(@typescript-eslint/no-shadow): ignore `{#snippet}` if it uses under component |
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,75 @@ | ||
--- | ||
pageClass: 'rule-details' | ||
sidebarDepth: 0 | ||
title: 'svelte/@typescript-eslint/no-shadow' | ||
description: 'Disallow variable declarations from shadowing variables declared in the outer scope' | ||
--- | ||
|
||
# svelte/@typescript-eslint/no-shadow | ||
|
||
> Disallow variable declarations from shadowing variables declared in the outer scope | ||
- :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 shadowed variables, similar to the base ESLint `@typescript-eslint/no-shadow` rule. However, it ignores cases where `{#snippet}` is used as a named slot in Svelte components. If this rule is active, make sure to disable the base `@typescript-eslint/no-shadow` and `svelte/no-shadow` and `no-shadow` rule, as it will conflict with this rule. | ||
|
||
<!--eslint-skip--> | ||
|
||
```svelte | ||
<script lang="ts"> | ||
/* eslint svelte/@typescript-eslint/no-shadow: "error" */ | ||
import ComponentWithSnippet from './ComponentWithSnippet.svelte'; | ||
</script> | ||
<!-- ✓ GOOD --> | ||
<ComponentWithSnippet> | ||
{#snippet children()} | ||
<AnotherComponentWithSnippet> | ||
{#snippet children()} | ||
Hello! | ||
{/snippet} | ||
</AnotherComponentWithSnippet> | ||
{/snippet} | ||
</ComponentWithSnippet> | ||
<!-- ✗ BAD --> | ||
<ComponentWithSnippet> | ||
{@const foo = 1} | ||
<ComponentWithSnippet> | ||
{@const foo = 2} | ||
</ComponentWithSnippet> | ||
</ComponentWithSnippet> | ||
``` | ||
|
||
## :wrench: Options | ||
|
||
```json | ||
{ | ||
"svelte/no-shadow": [ | ||
"error", | ||
{ "builtinGlobals": false, "hoist": "functions", "allow": [], "ignoreOnInitialization": false } | ||
] | ||
} | ||
``` | ||
|
||
- `builtinGlobals`: The `builtinGlobals` option is `false` by default. If it is `true`, the rule prevents shadowing of built-in global variables: `Object`, `Array`, `Number`, and so on. | ||
- `hoist`: The `hoist` option has three settings: | ||
- `functions` (by default) - reports shadowing before the outer functions are defined. | ||
- `all` - reports all shadowing before the outer variables/functions are defined. | ||
- `never` - never report shadowing before the outer variables/functions are defined. | ||
- `allow`: The `allow` option is an array of identifier names for which shadowing is allowed. For example, `"resolve"`, `"reject"`, `"done"`, `"cb"`. | ||
- `ignoreOnInitialization`: The `ignoreOnInitialization` option is `false` by default. If it is `true`, it prevents reporting shadowing of variables in their initializers when the shadowed variable is presumably still uninitialized. The shadowed variable must be on the left side. The shadowing variable must be on the right side and declared in a callback function or in an IIFE. | ||
- `ignoreTypeValueShadow`: Whether to ignore types named the same as a variable. Default: `true`. This is generally safe because you cannot use variables in type locations without a `typeof` operator, so there's little risk of confusion. | ||
- `ignoreFunctionTypeParameterNameValueShadow`: Whether to ignore function parameters named the same as a variable. Default: `true`. Each of a function type's arguments creates a value variable within the scope of the function type. This is done so that you can reference the type later using the `typeof` operator. | ||
|
||
## :books: Further Reading | ||
|
||
- See [typescript-eslint `no-shadow` rule](https://typescript-eslint.io/rules/no-shadow/) for more information about the base rule. | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-shadow.ts) | ||
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-shadow.ts) | ||
|
||
<sup>Taken with ❤️ [from @typescript-eslint/eslint-plugin](https://typescript-eslint.io/rules/no-shadow/)</sup> |
83 changes: 83 additions & 0 deletions
83
packages/eslint-plugin-svelte/src/rules/@typescript-eslint/no-shadow.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,83 @@ | ||
import { createRule } from '../../utils/index.js'; | ||
import { defineWrapperListener, getProxyContent, getCoreRule } from '../../utils/eslint-core.js'; | ||
import type { TSESTree } from '@typescript-eslint/types'; | ||
import type { Scope } from '@typescript-eslint/scope-manager'; | ||
import type { Range } from 'svelte-eslint-parser/lib/ast/common.js'; | ||
import { getScope as getScopeUtil } from '../../utils/ast-utils.js'; | ||
import { getSourceCode as getSourceCodeCompat } from '../../utils/compat.js'; | ||
|
||
const coreRule = getCoreRule('@typescript-eslint/no-shadow'); | ||
|
||
function removeSnippetIdentifiers(snippetIdentifierNodeLocations: Range[], scope: Scope): Scope { | ||
return { | ||
...scope, | ||
variables: scope.variables.filter((variable) => { | ||
return !snippetIdentifierNodeLocations.some(([start, end]) => { | ||
return variable.identifiers.every((identifier) => { | ||
const { range } = identifier; | ||
return range[0] === start && range[1] === end; | ||
}); | ||
}); | ||
}), | ||
childScopes: scope.childScopes.map((scope) => { | ||
return removeSnippetIdentifiers(snippetIdentifierNodeLocations, scope); | ||
}) | ||
} as Scope; | ||
} | ||
|
||
export default createRule('@typescript-eslint/no-shadow', { | ||
meta: { | ||
...coreRule.meta, | ||
docs: { | ||
description: coreRule.meta.docs.description, | ||
category: 'Best Practices', | ||
recommended: false, | ||
extensionRule: '@typescript-eslint/no-shadow' | ||
} | ||
}, | ||
create(context) { | ||
const snippetIdentifierNodeLocations: Range[] = []; | ||
|
||
function getScope(node: TSESTree.Node) { | ||
const scope = getScopeUtil(context, node); | ||
return removeSnippetIdentifiers(snippetIdentifierNodeLocations, scope); | ||
} | ||
|
||
function getSourceCode() { | ||
const sourceCode = getSourceCodeCompat(context); | ||
return new Proxy(sourceCode, { | ||
get(target, key) { | ||
if (key === 'getScope') { | ||
return getScope; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore | ||
return (target as any)[key]; | ||
} | ||
}); | ||
} | ||
|
||
return defineWrapperListener( | ||
coreRule, | ||
getProxyContent(context, { | ||
sourceCode: getSourceCode() | ||
}), | ||
{ | ||
createListenerProxy(coreListener) { | ||
return { | ||
...coreListener, | ||
SvelteSnippetBlock(node) { | ||
const parent = node.parent; | ||
if (parent.type === 'SvelteElement' && parent.kind === 'component') { | ||
snippetIdentifierNodeLocations.push(node.id.range); | ||
} | ||
coreListener.SvelteSnippetBlock?.(node); | ||
}, | ||
'Program:exit'(node) { | ||
coreListener['Program:exit']?.(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
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
4 changes: 4 additions & 0 deletions
4
...plugin-svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/invalid/basic-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: "'x' is already declared in the upper scope on line 2 column 13." | ||
line: 4 | ||
column: 8 | ||
suggestions: null |
7 changes: 7 additions & 0 deletions
7
...lugin-svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/invalid/basic-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 lang="ts"> | ||
function a(x: string) { | ||
var b = function c() { | ||
var x: string = 'foo'; | ||
}; | ||
} | ||
</script> |
4 changes: 4 additions & 0 deletions
4
...plugin-svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/invalid/const-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: "'foo' is already declared in the upper scope on line 6 column 10." | ||
line: 8 | ||
column: 11 | ||
suggestions: null |
10 changes: 10 additions & 0 deletions
10
...lugin-svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/invalid/const-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 lang="ts"> | ||
import ComponentWithSnippet from './ComponentWithSnippet.svelte'; | ||
</script> | ||
|
||
<ComponentWithSnippet> | ||
{@const foo = 1} | ||
<ComponentWithSnippet> | ||
{@const foo = 2} | ||
</ComponentWithSnippet> | ||
</ComponentWithSnippet> |
10 changes: 10 additions & 0 deletions
10
...-plugin-svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/valid/basic-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 lang="ts"> | ||
var a: number = 3; | ||
var b = (x) => { | ||
a++; | ||
return x + a; | ||
}; | ||
setTimeout(() => { | ||
b(a); | ||
}, 0); | ||
</script> |
13 changes: 13 additions & 0 deletions
13
...ugin-svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/valid/snippet1-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 ComponentWithSnippet from './ComponentWithSnippet.svelte'; | ||
</script> | ||
|
||
<ComponentWithSnippet> | ||
{#snippet children()} | ||
<AnotherComponentWithSnippet> | ||
{#snippet children()} | ||
Hello! | ||
{/snippet} | ||
</AnotherComponentWithSnippet> | ||
{/snippet} | ||
</ComponentWithSnippet> |
3 changes: 3 additions & 0 deletions
3
...svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/valid/snippet1-requirements.json
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 @@ | ||
{ | ||
"svelte": ">=5.0.0-0" | ||
} |
10 changes: 10 additions & 0 deletions
10
...ugin-svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/valid/snippet2-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 lang="ts"> | ||
import ComponentWithSnippet from './ComponentWithSnippet.svelte'; | ||
const children: number = 1; | ||
</script> | ||
|
||
<ComponentWithSnippet> | ||
{#snippet children()} | ||
Hello! | ||
{/snippet} | ||
</ComponentWithSnippet> |
3 changes: 3 additions & 0 deletions
3
...svelte/tests/fixtures/rules/@typescript-eslint/no-shadow/valid/snippet2-requirements.json
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 @@ | ||
{ | ||
"svelte": ">=5.0.0-0" | ||
} |
24 changes: 24 additions & 0 deletions
24
packages/eslint-plugin-svelte/tests/src/rules/@typescript-eslint/no-shadow.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,24 @@ | ||
import { RuleTester } from '../../../utils/eslint-compat.js'; | ||
import rule from '../../../../src/rules/@typescript-eslint/no-shadow.js'; | ||
import { loadTestCases, RULES_PROJECT } from '../../../utils/utils.js'; | ||
|
||
const tester = new RuleTester({ | ||
languageOptions: { | ||
ecmaVersion: 2020, | ||
sourceType: 'module', | ||
parserOptions: { | ||
parser: { | ||
ts: '@typescript-eslint/parser', | ||
js: 'espree' | ||
}, | ||
project: RULES_PROJECT, | ||
disallowAutomaticSingleRunInference: true | ||
} | ||
} | ||
}); | ||
|
||
tester.run( | ||
'@typescript-eslint/no-shadow', | ||
rule as any, | ||
loadTestCases('@typescript-eslint/no-shadow') | ||
); |