Skip to content

Commit 0f7bd88

Browse files
authored
fix: various snippet fixes (#2449)
#2444 #2417 #2425 #2385 (comment) The snippet is now transformed into a function with the return type so that The default parameter type can be inferred: `const foo = (a: string, b = 1): ReturnType<import('svelte').Snippet> => {}`
1 parent 4a9ef5e commit 0f7bd88

File tree

25 files changed

+368
-90
lines changed

25 files changed

+368
-90
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import {
9393
convertToLocationForReferenceOrDefinition,
9494
convertToLocationRange,
9595
isInScript,
96+
isSvelte2tsxShimFile,
9697
isSvelteFilePath,
9798
symbolKindFromString
9899
} from './utils';
@@ -387,7 +388,7 @@ export class TypeScriptPlugin
387388

388389
const result = await Promise.all(
389390
defs.definitions.map(async (def) => {
390-
if (def.fileName.endsWith('svelte-shims.d.ts')) {
391+
if (isSvelte2tsxShimFile(def.fileName)) {
391392
return;
392393
}
393394

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import {
1717
isInGeneratedCode,
1818
findChildOfKind,
1919
findRenderFunction,
20-
findClosestContainingNode,
21-
SnapshotMap
20+
SnapshotMap,
21+
startsWithIgnoredPosition
2222
} from './utils';
23-
import { convertRange } from '../utils';
23+
import { convertRange, isSvelte2tsxShimFile } from '../utils';
2424

2525
export class InlayHintProviderImpl implements InlayHintProvider {
2626
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}
@@ -195,10 +195,19 @@ export class InlayHintProviderImpl implements InlayHintProvider {
195195
return false;
196196
}
197197

198-
const node = findClosestContainingNode(
198+
if (inlayHint.displayParts?.some((v) => isSvelte2tsxShimFile(v.file))) {
199+
return true;
200+
}
201+
202+
const hasParameterWithSamePosition = (node: ts.CallExpression | ts.NewExpression) =>
203+
node.arguments !== undefined &&
204+
node.arguments.some((arg) => arg.getStart() === inlayHint.position);
205+
206+
const node = findContainingNode(
199207
sourceFile,
200208
{ start: inlayHint.position, length: 0 },
201-
ts.isCallOrNewExpression
209+
(node): node is ts.CallExpression | ts.NewExpression =>
210+
ts.isCallOrNewExpression(node) && hasParameterWithSamePosition(node)
202211
);
203212

204213
if (!node) {
@@ -224,6 +233,10 @@ export class InlayHintProviderImpl implements InlayHintProvider {
224233
return false;
225234
}
226235

236+
if (startsWithIgnoredPosition(sourceFile.text, inlayHint.position)) {
237+
return true;
238+
}
239+
227240
const declaration = findContainingNode(
228241
sourceFile,
229242
{ start: inlayHint.position, length: 0 },

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export function isComponentAtPosition(
8383

8484
export const IGNORE_START_COMMENT = '/*Ωignore_startΩ*/';
8585
export const IGNORE_END_COMMENT = '/*Ωignore_endΩ*/';
86+
export const IGNORE_POSITION_COMMENT = '/*Ωignore_positionΩ*/';
8687

8788
/**
8889
* Surrounds given string with a start/end comment which marks it
@@ -105,6 +106,10 @@ export function isInGeneratedCode(text: string, start: number, end: number = sta
105106
return (lastStart > lastEnd || lastEnd === nextEnd) && lastStart < nextEnd;
106107
}
107108

109+
export function startsWithIgnoredPosition(text: string, offset: number) {
110+
return text.slice(offset).startsWith(IGNORE_POSITION_COMMENT);
111+
}
112+
108113
/**
109114
* Checks if this is a text span that is inside svelte2tsx-generated code
110115
* (has no mapping to the original)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,7 @@ export function hasTsExtensions(fileName: string) {
371371
fileName.endsWith(ts.Extension.Ts)
372372
);
373373
}
374+
375+
export function isSvelte2tsxShimFile(fileName: string | undefined) {
376+
return fileName?.endsWith('svelte-shims.d.ts') || fileName?.endsWith('svelte-shims-v4.d.ts');
377+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script lang="ts">
2+
let { foo }: {
3+
foo: import('svelte').Snippet<[a: '']>
4+
children?: import('svelte').Snippet
5+
required: import('svelte').Snippet
6+
} = $props();
7+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"range": { "start": { "line": 4, "character": 1 }, "end": { "line": 4, "character": 14 } },
4+
"severity": 1,
5+
"source": "ts",
6+
"message": "Property 'required' is missing in type '{ children: () => any; foo: (this: void, a: \"\") => any; }' but required in type '$$ComponentProps'.",
7+
"code": 2741,
8+
"tags": []
9+
},
10+
{
11+
"range": { "start": { "line": 6, "character": 9 }, "end": { "line": 6, "character": 18 } },
12+
"severity": 1,
13+
"source": "ts",
14+
"message": "This comparison appears to be unintentional because the types '\"\"' and '\"b\"' have no overlap.",
15+
"code": 2367,
16+
"tags": []
17+
}
18+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script lang="ts">
2+
import SnippetParent from "./SnippetParent.svelte";
3+
</script>
4+
5+
<SnippetParent>
6+
{#snippet foo(a)}
7+
{a === 'b'}
8+
{/snippet}
9+
10+
{@render foo('')}
11+
</SnippetParent>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
{
3+
"range": {
4+
"start": { "line": 10, "character": 9 },
5+
"end": { "line": 10, "character": 18 }
6+
},
7+
"severity": 1,
8+
"source": "js",
9+
"message": "This comparison appears to be unintentional because the types 'number' and 'string' have no overlap.",
10+
"code": 2367,
11+
"tags": []
12+
},
13+
{
14+
"range": {
15+
"start": { "line": 16, "character": 12 },
16+
"end": { "line": 16, "character": 15 }
17+
},
18+
"severity": 1,
19+
"source": "js",
20+
"message": "Argument of type '\"c\"' is not assignable to parameter of type 'TypeA'.",
21+
"code": 2345,
22+
"tags": []
23+
}
24+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
// @ts-check
3+
/**
4+
* @typedef {'a' | 'b'} TypeA
5+
*/
6+
</script>
7+
8+
<!--no error-->
9+
{#snippet hi(/**@type {TypeA}*/a, b = 2)}
10+
{a}
11+
{#if b === 'a'}
12+
{b}
13+
{/if}
14+
{/snippet}
15+
16+
<!--error-->
17+
{@render hi('c', 'd')}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[
2+
{
3+
"range": {
4+
"start": { "line": 15, "character": 9 },
5+
"end": { "line": 15, "character": 16 }
6+
},
7+
"severity": 1,
8+
"source": "ts",
9+
"message": "Cannot find name 'nested1'.",
10+
"code": 2304,
11+
"tags": []
12+
}
13+
]

0 commit comments

Comments
 (0)