Skip to content

Commit

Permalink
breaking(svelte5): only generate function component shape in runes mo…
Browse files Browse the repository at this point in the history
…de (#2517)

When a component is in runes mode and not using slots or events, adjust the output to only create the function type that mimics the underlying real shape of components in Svelte 5. This is a breaking change because previously the type was enhanced such that it also had the legacy class shape. As a result, users now may need to switch to `typeof Component` when using the component inside types.

Sadly, due to a combination of requirements and TypeScript limitations, we need to always create both a legacy class component and function component type.
 - Constraints: Need to support Svelte 4 class component types, therefore we need to use __sveltets_2_ensureComponent to transform function components to classes
- Limitations: TypeScript is not able to preserve generics during said transformation (i.e. there's no way to express keeping the generic etc)

TODO Svelte 6/7: Switch this around and not use new Component in svelte2tsx anymore, which means we can remove the legacy class component. We need something like _ensureFnComponent then.
  • Loading branch information
dummdidumm authored Sep 26, 2024
1 parent 35af691 commit 837b61f
Show file tree
Hide file tree
Showing 30 changed files with 285 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,29 +66,23 @@ export class SemanticTokensProviderImpl implements SemanticTokensProvider {
continue;
}

const originalPosition = this.mapToOrigin(
const original = this.map(
textDocument,
tsDoc,
generatedOffset,
generatedLength,
encodedClassification
encodedClassification,
classificationType
);
if (!originalPosition) {
continue;
}

const [line, character, length] = originalPosition;

// remove identifiers whose start and end mapped to the same location,
// like the svelte2tsx inserted render function,
// or reversed like Component.$on
if (length <= 0) {
if (!original || original[2] <= 0) {
continue;
}

const modifier = this.getTokenModifierFromClassification(encodedClassification);

data.push([line, character, length, classificationType, modifier]);
data.push(original);
}

const sorted = data.sort((a, b) => {
Expand All @@ -103,17 +97,20 @@ export class SemanticTokensProviderImpl implements SemanticTokensProvider {
return builder.build();
}

private mapToOrigin(
private map(
document: Document,
snapshot: SvelteDocumentSnapshot,
generatedOffset: number,
generatedLength: number,
token: number
): [line: number, character: number, length: number, start: number] | undefined {
encodedClassification: number,
classificationType: number
):
| [line: number, character: number, length: number, token: number, modifier: number]
| undefined {
const text = snapshot.getFullText();
if (
isInGeneratedCode(text, generatedOffset, generatedOffset + generatedLength) ||
(token === 2817 /* top level function */ &&
(encodedClassification === 2817 /* top level function */ &&
text.substring(generatedOffset, generatedOffset + generatedLength) === 'render')
) {
return;
Expand All @@ -132,7 +129,26 @@ export class SemanticTokensProviderImpl implements SemanticTokensProvider {
const startOffset = document.offsetAt(startPosition);
const endOffset = document.offsetAt(endPosition);

return [startPosition.line, startPosition.character, endOffset - startOffset, startOffset];
// Ensure components in the template get no semantic highlighting
if (
(classificationType === 0 ||
classificationType === 5 ||
classificationType === 7 ||
classificationType === 10) &&
snapshot.svelteNodeAt(startOffset)?.type === 'InlineComponent' &&
(document.getText().charCodeAt(startOffset - 1) === /* < */ 60 ||
document.getText().charCodeAt(startOffset - 1) === /* / */ 47)
) {
return;
}

return [
startPosition.line,
startPosition.character,
endOffset - startOffset,
classificationType,
this.getTokenModifierFromClassification(encodedClassification)
];
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,6 @@ describe('SemanticTokensProvider', function () {
type: TokenType.variable,
modifiers: [TokenModifier.declaration, TokenModifier.local, TokenModifier.readonly]
},
{
line: 12,
character: 5,
length: 'Imported'.length,
type: isSvelte5Plus ? TokenType.type : TokenType.class,
modifiers: isSvelte5Plus ? [TokenModifier.readonly] : []
},
{
line: 12,
character: 23,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script lang="ts" generics="T">
let { readonly, can_bind = $bindable() }: { readonly?: T; can_bind?: T } = $props();
export function only_bind() {
return true;
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
"range": {
"end": {
"character": 20,
"line": 25
"line": 26
},
"start": {
"character": 7,
"line": 25
"line": 26
}
},
"severity": 1,
Expand All @@ -22,11 +22,11 @@
"range": {
"end": {
"character": 21,
"line": 26
"line": 27
},
"start": {
"character": 12,
"line": 26
"line": 27
}
},
"severity": 1,
Expand All @@ -39,11 +39,11 @@
"range": {
"end": {
"character": 21,
"line": 26
"line": 27
},
"start": {
"character": 7,
"line": 26
"line": 27
}
},
"severity": 1,
Expand All @@ -56,11 +56,79 @@
"range": {
"end": {
"character": 17,
"line": 27
"line": 28
},
"start": {
"character": 8,
"line": 27
"line": 28
}
},
"severity": 1,
"source": "ts",
"tags": []
},
{
"code": 2322,
"message": "Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\nTo mark a property as bindable: 'let { readonly = $bindable() } = $props()'",
"range": {
"end": {
"character": 27,
"line": 30
},
"start": {
"character": 14,
"line": 30
}
},
"severity": 1,
"source": "ts",
"tags": []
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'only_bind' does not exist in type '$$ComponentProps'.",
"range": {
"end": {
"character": 28,
"line": 31
},
"start": {
"character": 19,
"line": 31
}
},
"severity": 1,
"source": "ts",
"tags": []
},
{
"code": 2322,
"message": "Cannot use 'bind:' with this property. It is declared as non-bindable inside the component.\nTo mark a property as bindable: 'let { only_bind = $bindable() } = $props()'",
"range": {
"end": {
"character": 28,
"line": 31
},
"start": {
"character": 14,
"line": 31
}
},
"severity": 1,
"source": "ts",
"tags": []
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'only_bind' does not exist in type '$$ComponentProps'.",
"range": {
"end": {
"character": 24,
"line": 32
},
"start": {
"character": 15,
"line": 32
}
},
"severity": 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
[
{
"code": 2344,
"message": "Type 'typeof Runes__SvelteComponent_' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof Runes__SvelteComponent_' provides no match for the signature '(...args: any): any'.",
"range": {
"end": {
"character": 41,
"line": 12
},
"start": {
"character": 29,
"line": 12
}
},
"severity": 1,
"source": "ts",
"tags": []
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'can_bind' does not exist in type '{ only_bind?: (() => boolean) | undefined; }'.",
"range": {
"end": {
"character": 20,
"line": 20
"line": 21
},
"start": {
"character": 12,
"line": 20
"line": 21
}
},
"severity": 1,
Expand All @@ -22,11 +39,11 @@
"range": {
"end": {
"character": 16,
"line": 21
"line": 22
},
"start": {
"character": 8,
"line": 21
"line": 22
}
},
"severity": 1,
Expand All @@ -39,11 +56,11 @@
"range": {
"end": {
"character": 16,
"line": 22
"line": 23
},
"start": {
"character": 8,
"line": 22
"line": 23
}
},
"severity": 1,
Expand All @@ -56,11 +73,28 @@
"range": {
"end": {
"character": 20,
"line": 25
"line": 26
},
"start": {
"character": 12,
"line": 25
"line": 26
}
},
"severity": 1,
"source": "ts",
"tags": []
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'readonly' does not exist in type '{ only_bind?: (() => boolean) | undefined; }'.",
"range": {
"end": {
"character": 27,
"line": 30
},
"start": {
"character": 19,
"line": 30
}
},
"severity": 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import Legacy from './Legacy.svelte';
import Runes from './Runes.svelte';
import RunesGeneric from './RunesGeneric.svelte';
let bind_and_prop: () => boolean;
let value = '';
Expand All @@ -9,7 +10,7 @@
let can_bind = '';
let readonly = ''
let instance: Runes;
let instance: ReturnType<typeof Runes>;
instance!.only_bind() === true;
</script>

Expand All @@ -26,3 +27,7 @@
<Runes bind:readonly />
<Runes bind:only_bind />
<Runes {only_bind} />

<RunesGeneric bind:readonly />
<RunesGeneric bind:only_bind />
<RunesGeneric {only_bind} />
Loading

0 comments on commit 837b61f

Please sign in to comment.