-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
svelte/@typescript-eslint/no-unnecessary-condition
rule (#…
…262) * feat: wpi no-unnecessary-condition * docs: support type info in demo site * fix: rule * fix: revert eslint ignore * Create real-wasps-punch.md * fix: ignore eslint
- Loading branch information
Showing
49 changed files
with
3,310 additions
and
42 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": minor | ||
--- | ||
|
||
feat: add `svelte/@typescript-eslint/no-unnecessary-condition` rule |
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
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
101 changes: 101 additions & 0 deletions
101
docs-svelte-kit/src/lib/eslint/scripts/ts-create-program.mts
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,101 @@ | ||
import type typescript from "typescript" | ||
import type tsvfs from "@typescript/vfs" | ||
type TS = typeof typescript | ||
type TSVFS = typeof tsvfs | ||
|
||
/** Create Program */ | ||
export function createProgram( | ||
{ | ||
ts, | ||
compilerOptions, | ||
compilerHost, | ||
}: { | ||
ts: TS | ||
compilerOptions: typescript.CompilerOptions | ||
compilerHost: typescript.CompilerHost | ||
}, | ||
options: { filePath: string }, | ||
): typescript.Program { | ||
try { | ||
const program = ts.createProgram({ | ||
rootNames: [options.filePath], | ||
options: compilerOptions, | ||
host: compilerHost, | ||
}) | ||
return program | ||
} catch (e) { | ||
// eslint-disable-next-line no-console -- Demo debug | ||
console.error(e) | ||
throw e | ||
} | ||
} | ||
|
||
export function createCompilerOptions(ts: TS): typescript.CompilerOptions { | ||
const compilerOptions: typescript.CompilerOptions = { | ||
target: ts.ScriptTarget.ESNext, | ||
module: ts.ModuleKind.ESNext, | ||
jsx: ts.JsxEmit.Preserve, | ||
strict: true, | ||
} | ||
compilerOptions.lib = [ts.getDefaultLibFileName(compilerOptions)] | ||
return compilerOptions | ||
} | ||
|
||
export async function createVirtualCompilerHost( | ||
{ | ||
ts, | ||
tsvfs, | ||
compilerOptions, | ||
}: { | ||
ts: TS | ||
tsvfs: TSVFS | ||
compilerOptions: typescript.CompilerOptions | ||
}, | ||
{ filePath: targetFilePath }: { filePath: string }, | ||
): Promise<{ | ||
compilerHost: typescript.CompilerHost | ||
updateFile: (sourceFile: typescript.SourceFile) => boolean | ||
fsMap: Map<string, string> | ||
}> { | ||
const fsMap = await tsvfs.createDefaultMapFromCDN( | ||
{ | ||
lib: Array.from( | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- use internal | ||
(ts as any).libMap.keys(), | ||
), | ||
}, | ||
ts.version, | ||
true, | ||
ts, | ||
) | ||
const system = tsvfs.createSystem(fsMap) | ||
const host = tsvfs.createVirtualCompilerHost(system, compilerOptions, ts) | ||
// eslint-disable-next-line @typescript-eslint/unbound-method -- backup original | ||
const original = { getSourceFile: host.compilerHost.getSourceFile } | ||
host.compilerHost.getSourceFile = function ( | ||
fileName, | ||
languageVersionOrOptions, | ||
...args | ||
) { | ||
if (targetFilePath === fileName) { | ||
// Exclude the target file from caching as it will be modified. | ||
const file = this.readFile(fileName) ?? "" | ||
return ts.createSourceFile(fileName, file, languageVersionOrOptions, true) | ||
} | ||
if (this.fileExists(fileName)) { | ||
return original.getSourceFile.apply(this, [ | ||
fileName, | ||
languageVersionOrOptions, | ||
...args, | ||
]) | ||
} | ||
// Avoid error | ||
// eslint-disable-next-line no-console -- Demo debug | ||
console.log(`Not exists: ${fileName}`) | ||
return undefined | ||
} | ||
return { | ||
...host, | ||
fsMap, | ||
} | ||
} |
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,54 @@ | ||
import { loadMonacoEngine } from "./monaco-loader" | ||
import { | ||
createProgram, | ||
createCompilerOptions, | ||
createVirtualCompilerHost, | ||
} from "./ts-create-program.mts" | ||
|
||
let tsParserCache = null | ||
export function loadTsParser() { | ||
return (tsParserCache ??= loadTsParserImpl()) | ||
} | ||
|
||
async function loadTsParserImpl() { | ||
await loadMonacoEngine() | ||
const [ts, tsvfs, tsParser] = await Promise.all([ | ||
import("typescript"), | ||
import("@typescript/vfs"), | ||
import("@typescript-eslint/parser"), | ||
]) | ||
if (typeof window === "undefined") { | ||
return tsParser | ||
} | ||
window.define("typescript", ts) | ||
|
||
const compilerOptions = createCompilerOptions(ts) | ||
const filePath = "/demo.ts" | ||
const host = await createVirtualCompilerHost( | ||
{ ts, tsvfs, compilerOptions }, | ||
{ filePath }, | ||
) | ||
return { | ||
parseForESLint(code, options) { | ||
host.fsMap.set(filePath, code) | ||
// Requires its own Program instance to provide full type information. | ||
const program = createProgram( | ||
{ ts, compilerHost: host.compilerHost, compilerOptions }, | ||
{ filePath }, | ||
) | ||
|
||
try { | ||
const result = tsParser.parseForESLint(code, { | ||
...options, | ||
filePath: filePath.replace(/^\//u, ""), | ||
programs: [program], | ||
}) | ||
return result | ||
} catch (e) { | ||
// eslint-disable-next-line no-console -- Demo debug | ||
console.error(e) | ||
throw e | ||
} | ||
}, | ||
} | ||
} |
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,68 @@ | ||
--- | ||
pageClass: "rule-details" | ||
sidebarDepth: 0 | ||
title: "svelte/@typescript-eslint/no-unnecessary-condition" | ||
description: "disallow conditionals where the type is always truthy or always falsy" | ||
--- | ||
|
||
# svelte/@typescript-eslint/no-unnecessary-condition | ||
|
||
> disallow conditionals where the type is always truthy or always falsy | ||
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge> | ||
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. | ||
|
||
## :book: Rule Details | ||
|
||
This rule extends the base `@typescript-eslint`'s [@typescript-eslint/no-unnecessary-condition] rule. | ||
The [@typescript-eslint/no-unnecessary-condition] rule does not understand reactive or rerendering of Svelte components and has false positives when used with Svelte components. This rule understands reactive and rerendering of Svelte components. | ||
|
||
<ESLintCodeBlock fix> | ||
|
||
<!--eslint-skip--> | ||
|
||
```svelte | ||
<script lang="ts"> | ||
/* eslint svelte/@typescript-eslint/no-unnecessary-condition: "error" */ | ||
export let foo: number | null = null | ||
/* ✗ BAD */ | ||
let b = foo || 42 | ||
/* ✓ GOOD */ | ||
$: a = foo || 42 | ||
</script> | ||
<!-- ✓ GOOD --> | ||
{foo || 42} | ||
``` | ||
|
||
</ESLintCodeBlock> | ||
|
||
## :wrench: Options | ||
|
||
```json | ||
{ | ||
"@typescript-eslint/no-unnecessary-condition": "off", | ||
"svelte/@typescript-eslint/no-unnecessary-condition": [ | ||
"error", | ||
{ | ||
"allowConstantLoopConditions": false, | ||
"allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing": false | ||
} | ||
] | ||
} | ||
``` | ||
|
||
Same as [@typescript-eslint/no-unnecessary-condition] rule option. See [here](https://typescript-eslint.io/rules/no-unnecessary-condition/#options) for details. | ||
|
||
## :couple: Related rules | ||
|
||
- [@typescript-eslint/no-unnecessary-condition] | ||
|
||
[@typescript-eslint/no-unnecessary-condition]: https://typescript-eslint.io/rules/no-unnecessary-condition/ | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/@typescript-eslint/no-unnecessary-condition.ts) | ||
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/@typescript-eslint/no-unnecessary-condition.ts) | ||
|
||
<sup>Taken with ❤️ [from @typescript-eslint/eslint-plugin](https://typescript-eslint.io/rules/no-unnecessary-condition/)</sup> |
Oops, something went wrong.