Skip to content

Commit c55114b

Browse files
authored
Fix unused false positives (#377)
* (fix) Swallow unused false positives `svelte2tsx` now also returns `exportedNames` through which we can easily get the props. #277
1 parent 3911274 commit c55114b

File tree

6 files changed

+55
-25
lines changed

6 files changed

+55
-25
lines changed

packages/language-server/src/plugins/typescript/DocumentSnapshot.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { RawSourceMap, SourceMapConsumer } from 'source-map';
2-
import svelte2tsx from 'svelte2tsx';
2+
import svelte2tsx, { IExportedNames } from 'svelte2tsx';
33
import ts from 'typescript';
44
import { Position, Range } from 'vscode-languageserver';
55
import {
@@ -82,17 +82,22 @@ export namespace DocumentSnapshot {
8282
* @param options options that apply to the svelte document
8383
*/
8484
export function fromDocument(document: Document, options: SvelteSnapshotOptions) {
85-
const { tsxMap, text, parserError, nrPrependedLines, scriptKind } = preprocessSvelteFile(
86-
document,
87-
options,
88-
);
85+
const {
86+
tsxMap,
87+
text,
88+
exportedNames,
89+
parserError,
90+
nrPrependedLines,
91+
scriptKind,
92+
} = preprocessSvelteFile(document, options);
8993

9094
return new SvelteDocumentSnapshot(
9195
document,
9296
parserError,
9397
scriptKind,
9498
text,
9599
nrPrependedLines,
100+
exportedNames,
96101
tsxMap,
97102
);
98103
}
@@ -121,6 +126,7 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions
121126
let parserError: ParserError | null = null;
122127
let nrPrependedLines = 0;
123128
let text = document.getText();
129+
let exportedNames: IExportedNames = { has: () => false };
124130

125131
const scriptKind = [
126132
getScriptKindFromAttributes(document.scriptInfo?.attributes ?? {}),
@@ -137,6 +143,7 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions
137143
});
138144
text = tsx.code;
139145
tsxMap = tsx.map;
146+
exportedNames = tsx.exportedNames;
140147
if (tsxMap) {
141148
tsxMap.sources = [document.uri];
142149

@@ -164,7 +171,7 @@ function preprocessSvelteFile(document: Document, options: SvelteSnapshotOptions
164171
text = document.scriptInfo ? document.scriptInfo.content : '';
165172
}
166173

167-
return { tsxMap, text, parserError, nrPrependedLines, scriptKind };
174+
return { tsxMap, text, exportedNames, parserError, nrPrependedLines, scriptKind };
168175
}
169176

170177
/**
@@ -181,6 +188,7 @@ export class SvelteDocumentSnapshot implements DocumentSnapshot {
181188
public readonly scriptKind: ts.ScriptKind,
182189
private readonly text: string,
183190
private readonly nrPrependedLines: number,
191+
private readonly exportedNames: IExportedNames,
184192
private readonly tsxMap?: RawSourceMap,
185193
) {}
186194

@@ -204,6 +212,10 @@ export class SvelteDocumentSnapshot implements DocumentSnapshot {
204212
return positionAt(offset, this.text);
205213
}
206214

215+
hasProp(name: string): boolean {
216+
return this.exportedNames.has(name);
217+
}
218+
207219
async getFragment() {
208220
if (!this.fragment) {
209221
const uri = pathToUrl(this.filePath);

packages/language-server/src/plugins/typescript/features/DiagnosticsProvider.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Document, mapDiagnosticToOriginal, getTextInRange } from '../../../lib/
44
import { DiagnosticsProvider } from '../../interfaces';
55
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
66
import { convertRange, mapSeverity } from '../utils';
7+
import { SvelteDocumentSnapshot } from '../DocumentSnapshot';
78

89
export class DiagnosticsProviderImpl implements DiagnosticsProvider {
910
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}
@@ -43,7 +44,7 @@ export class DiagnosticsProviderImpl implements DiagnosticsProvider {
4344
}))
4445
.map((diagnostic) => mapDiagnosticToOriginal(fragment, diagnostic))
4546
.filter(hasNoNegativeLines)
46-
.filter(isNoFalsePositive(document.getText()));
47+
.filter(isNoFalsePositive(document.getText(), tsDoc));
4748
}
4849

4950
private getLSAndTSDoc(document: Document) {
@@ -60,12 +61,12 @@ function hasNoNegativeLines(diagnostic: Diagnostic): boolean {
6061
return diagnostic.range.start.line >= 0 && diagnostic.range.end.line >= 0;
6162
}
6263

63-
function isNoFalsePositive(text: string) {
64+
function isNoFalsePositive(text: string, tsDoc: SvelteDocumentSnapshot) {
6465
return (diagnostic: Diagnostic) => {
6566
return (
6667
isNoJsxCannotHaveMultipleAttrsError(diagnostic) &&
6768
isNoUnusedLabelWarningForReactiveStatement(diagnostic) &&
68-
isNoUsedBeforeAssigned(diagnostic, text)
69+
isNoUsedBeforeAssigned(diagnostic, text, tsDoc)
6970
);
7071
};
7172
}
@@ -75,13 +76,16 @@ function isNoFalsePositive(text: string) {
7576
* without assigning a value in strict mode. Should not throw an error here
7677
* but on the component-user-side ("you did not set a required prop").
7778
*/
78-
function isNoUsedBeforeAssigned(diagnostic: Diagnostic, text: string): boolean {
79+
function isNoUsedBeforeAssigned(
80+
diagnostic: Diagnostic,
81+
text: string,
82+
tsDoc: SvelteDocumentSnapshot,
83+
): boolean {
7984
if (diagnostic.code !== 2454) {
8085
return true;
8186
}
8287

83-
const exportLetRegex = new RegExp(`export\\s+let\\s+${getTextInRange(diagnostic.range, text)}`);
84-
return !exportLetRegex.test(text);
88+
return !tsDoc.hasProp(getTextInRange(diagnostic.range, text));
8589
}
8690

8791
/**
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<script lang="typescript">
2-
export let noUsedBeforeDeclare: number;
2+
export let noUsedBeforeDeclare: number, anotherUsed: boolean;
33
const blubb = 2;
44
$: bla = blubb * 2;
55
</script>
66
<p on:click on:click></p>
77
{bla}
8-
{noUsedBeforeDeclare}
8+
{noUsedBeforeDeclare}
9+
{anotherUsed}

packages/svelte2tsx/index.d.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
type SvelteCompiledToTsx = {
2-
code: string,
3-
map: import("magic-string").SourceMap
1+
export interface SvelteCompiledToTsx {
2+
code: string;
3+
map: import("magic-string").SourceMap;
4+
exportedNames: IExportedNames;
5+
}
6+
7+
export interface IExportedNames {
8+
has(name: string): boolean;
49
}
510

611
export default function svelte2tsx(

packages/svelte2tsx/src/nodes/ExportedNames.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
export class ExportedNames extends Map<
2-
string,
3-
{
4-
type?: string;
5-
identifierText?: string;
6-
required?: boolean;
7-
}
8-
> {
1+
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
2+
export interface IExportedNames {
3+
has(name: string): boolean;
4+
}
5+
6+
export class ExportedNames
7+
extends Map<
8+
string,
9+
{
10+
type?: string;
11+
identifierText?: string;
12+
required?: boolean;
13+
}
14+
>
15+
implements IExportedNames {
916
/**
1017
* Creates a string from the collected props
1118
*

packages/svelte2tsx/src/svelte2tsx.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,5 +1039,6 @@ export function svelte2tsx(
10391039
return {
10401040
code: str.toString(),
10411041
map: str.generateMap({ hires: true, source: options?.filename }),
1042+
exportedNames
10421043
};
10431044
}

0 commit comments

Comments
 (0)