Skip to content

Commit c942382

Browse files
fix: properly transform $props.id when $props is assigned to props (#2694)
* fix: properly transform `$props.id` when `$props` is assigned to `props` * chore: add comments
1 parent 8af8141 commit c942382

File tree

9 files changed

+117
-4
lines changed

9 files changed

+117
-4
lines changed

packages/svelte2tsx/src/svelte2tsx/processInstanceScriptContent.ts

+41-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import MagicString from 'magic-string';
22
import { Node } from 'estree-walker';
3-
import ts from 'typescript';
3+
import ts, { VariableDeclaration } from 'typescript';
44
import { getBinaryAssignmentExpr, isNotPropertyNameOfImport, moveNode } from './utils/tsAst';
55
import { ExportedNames, is$$PropsDeclaration } from './nodes/ExportedNames';
66
import { ImplicitTopLevelNames } from './nodes/ImplicitTopLevelNames';
@@ -32,6 +32,7 @@ interface PendingStoreResolution {
3232
node: ts.Identifier;
3333
parent: ts.Node;
3434
scope: Scope;
35+
isPropsId: boolean;
3536
}
3637

3738
export function processInstanceScriptContent(
@@ -82,13 +83,19 @@ export function processInstanceScriptContent(
8283
//track if we are in a declaration scope
8384
let isDeclaration = false;
8485

86+
//track the variable declaration node
87+
let variableDeclarationNode: VariableDeclaration | null = null;
88+
8589
//track $store variables since we are only supposed to give top level scopes special treatment, and users can declare $blah variables at higher scopes
8690
//which prevents us just changing all instances of Identity that start with $
87-
const pendingStoreResolutions: PendingStoreResolution[] = [];
91+
let pendingStoreResolutions: PendingStoreResolution[] = [];
8892

8993
let scope = new Scope();
9094
const rootScope = scope;
9195

96+
//track is the variable declared as `props` comes from `$props()`
97+
let isPropsDeclarationRune = false;
98+
9299
const pushScope = () => (scope = new Scope(scope));
93100
const popScope = () => (scope = scope.parent);
94101

@@ -124,6 +131,17 @@ export function processInstanceScriptContent(
124131
return;
125132
}
126133

134+
//if we are in a variable declaration and the identifier is `props` we check the initializer
135+
if (
136+
ident.text === 'props' &&
137+
variableDeclarationNode &&
138+
variableDeclarationNode.initializer &&
139+
ts.isCallExpression(variableDeclarationNode.initializer) &&
140+
variableDeclarationNode.initializer.getText() === '$props()'
141+
) {
142+
isPropsDeclarationRune = true;
143+
}
144+
127145
if (isDeclaration || ts.isParameter(parent)) {
128146
if (
129147
isNotPropertyNameOfImport(ident) &&
@@ -148,14 +166,25 @@ export function processInstanceScriptContent(
148166
!ts.isTypeAliasDeclaration(parent) &&
149167
!ts.isInterfaceDeclaration(parent)
150168
) {
169+
let isPropsId = false;
170+
if (
171+
text === '$props' &&
172+
ts.isPropertyAccessExpression(parent) &&
173+
parent.parent &&
174+
ts.isCallExpression(parent.parent) &&
175+
parent.parent.arguments.length === 0
176+
) {
177+
const text = parent.getText();
178+
isPropsId = text === '$props.id';
179+
}
151180
// Handle the const { ...props } = $props() case
152181
const is_rune =
153182
(text === '$props' || text === '$derived' || text === '$state') &&
154183
ts.isCallExpression(parent) &&
155184
ts.isVariableDeclaration(parent.parent) &&
156185
parent.parent.name.getText().includes(text.slice(1));
157186
if (!is_rune) {
158-
pendingStoreResolutions.push({ node: ident, parent, scope });
187+
pendingStoreResolutions.push({ node: ident, parent, scope, isPropsId });
159188
}
160189
}
161190
}
@@ -234,7 +263,11 @@ export function processInstanceScriptContent(
234263

235264
if (ts.isVariableDeclaration(parent) && parent.name == node) {
236265
isDeclaration = true;
237-
onLeaveCallbacks.push(() => (isDeclaration = false));
266+
variableDeclarationNode = parent;
267+
onLeaveCallbacks.push(() => {
268+
isDeclaration = false;
269+
variableDeclarationNode = null;
270+
});
238271
}
239272

240273
if (ts.isBindingElement(parent) && parent.name == node) {
@@ -295,6 +328,10 @@ export function processInstanceScriptContent(
295328
tsAst.forEachChild((n) => walk(n, tsAst));
296329

297330
//resolve stores
331+
if (isPropsDeclarationRune) {
332+
//we filter out every pendingStore resolution that `isPropsId` if the variable names `props` comes from `$props()`
333+
pendingStoreResolutions = pendingStoreResolutions.filter(({ isPropsId }) => !isPropsId);
334+
}
298335
pendingStoreResolutions.map(resolveStore);
299336

300337
// declare implicit reactive variables we found in the script
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
///<reference types="svelte" />
2+
;function $$render() {
3+
4+
let/** @typedef {{ props: any }} $$ComponentProps *//** @type {$$ComponentProps} */ { props } = $props();
5+
let id = $props.id();
6+
;
7+
async () => {
8+
9+
id; props;};
10+
return { props: /** @type {$$ComponentProps} */({}), exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
11+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
12+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
13+
export default Input__SvelteComponent_;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
let { props } = $props();
3+
let id = $props.id();
4+
</script>
5+
6+
{id} {props}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
///<reference types="svelte" />
2+
;function $$render() {
3+
4+
let props = {}/*Ωignore_startΩ*/;let $props = __sveltets_2_store_get(props);/*Ωignore_endΩ*/;
5+
let id = $props.id();
6+
;
7+
async () => {
8+
9+
id; props;};
10+
return { props: /** @type {Record<string, never>} */ ({}), exports: {}, bindings: "", slots: {}, events: {} }}
11+
const Input__SvelteComponent_ = __sveltets_2_isomorphic_component(__sveltets_2_partial(__sveltets_2_with_any_event($$render())));
12+
/*Ωignore_startΩ*/type Input__SvelteComponent_ = InstanceType<typeof Input__SvelteComponent_>;
13+
/*Ωignore_endΩ*/export default Input__SvelteComponent_;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
let props = {};
3+
let id = $props.id();
4+
</script>
5+
6+
{id} {props}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
///<reference types="svelte" />
2+
;function $$render() {
3+
4+
let/** @typedef {Record<string, any>} $$ComponentProps *//** @type {$$ComponentProps} */ {...props} = $props();
5+
let id = $props.id();
6+
;
7+
async () => {
8+
9+
id; props;};
10+
return { props: /** @type {$$ComponentProps} */({}), exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
11+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
12+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
13+
export default Input__SvelteComponent_;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
let {...props} = $props();
3+
let id = $props.id();
4+
</script>
5+
6+
{id} {props}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
///<reference types="svelte" />
2+
;function $$render() {
3+
4+
let props = $props();
5+
let id = $props.id();
6+
;
7+
async () => {
8+
9+
id; props;};
10+
return { props: /** @type {$$ComponentProps} */({}), exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
11+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
12+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
13+
export default Input__SvelteComponent_;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
let props = $props();
3+
let id = $props.id();
4+
</script>
5+
6+
{id} {props}

0 commit comments

Comments
 (0)