Skip to content

Commit 5456e53

Browse files
authored
fix: various hoistability check (#2740)
#2671 #2727
1 parent fa746fb commit 5456e53

File tree

17 files changed

+309
-7
lines changed

17 files changed

+309
-7
lines changed

packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ export class HoistableInterfaces {
8686
if (ts.isInterfaceDeclaration(node)) {
8787
this.module_types.add(node.name.text);
8888
}
89+
90+
if (ts.isEnumDeclaration(node)) {
91+
this.module_types.add(node.name.text);
92+
}
93+
94+
if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name)) {
95+
this.module_types.add(node.name.text);
96+
}
8997
}
9098

9199
analyzeInstanceScriptNode(node: ts.Node) {
@@ -158,6 +166,24 @@ export class HoistableInterfaces {
158166
}
159167
});
160168

169+
node.heritageClauses?.forEach((clause) => {
170+
clause.types.forEach((type) => {
171+
if (ts.isIdentifier(type.expression)) {
172+
const type_name = type.expression.text;
173+
if (!generics.includes(type_name)) {
174+
type_dependencies.add(type_name);
175+
}
176+
}
177+
178+
this.collectTypeDependencies(
179+
type,
180+
type_dependencies,
181+
value_dependencies,
182+
generics
183+
);
184+
});
185+
});
186+
161187
if (this.module_types.has(interface_name)) {
162188
// shadowed; we can't hoist
163189
this.disallowed_types.add(interface_name);
@@ -229,6 +255,14 @@ export class HoistableInterfaces {
229255
if (ts.isEnumDeclaration(node)) {
230256
this.disallowed_values.add(node.name.text);
231257
}
258+
259+
// namespace declaration should not be in the instance script.
260+
// Only adding the top-level name to the disallowed list,
261+
// so that at least there won't a confusing error message of "can't find namespace Foo"
262+
if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name)) {
263+
this.disallowed_types.add(node.name.text);
264+
this.disallowed_values.add(node.name.text);
265+
}
232266
}
233267

234268
analyze$propsRune(
@@ -239,7 +273,7 @@ export class HoistableInterfaces {
239273
if (node.initializer.typeArguments?.length > 0 || node.type) {
240274
const generic_arg = node.initializer.typeArguments?.[0] || node.type;
241275
if (ts.isTypeReferenceNode(generic_arg)) {
242-
const name = this.getEntityNameText(generic_arg.typeName);
276+
const name = this.getEntityNameRoot(generic_arg.typeName);
243277
const interface_node = this.interface_map.get(name);
244278
if (interface_node) {
245279
this.props_interface.name = name;
@@ -394,13 +428,13 @@ export class HoistableInterfaces {
394428
) {
395429
const walk = (node: ts.Node) => {
396430
if (ts.isTypeReferenceNode(node)) {
397-
const type_name = this.getEntityNameText(node.typeName);
431+
const type_name = this.getEntityNameRoot(node.typeName);
398432
if (!generics.includes(type_name)) {
399433
type_dependencies.add(type_name);
400434
}
401435
} else if (ts.isTypeQueryNode(node)) {
402436
// Handle 'typeof' expressions: e.g., foo: typeof bar
403-
value_dependencies.add(this.getEntityNameText(node.exprName));
437+
value_dependencies.add(this.getEntityNameRoot(node.exprName));
404438
}
405439

406440
ts.forEachChild(node, walk);
@@ -410,15 +444,16 @@ export class HoistableInterfaces {
410444
}
411445

412446
/**
413-
* Retrieves the full text of an EntityName (handles nested names).
447+
* Retrieves the top-level variable/namespace of an EntityName (handles nested names).
448+
* ex: `foo.bar.baz` -> `foo`
414449
* @param entity_name The EntityName to extract text from.
415-
* @returns The full name as a string.
450+
* @returns The top-level name as a string.
416451
*/
417-
private getEntityNameText(entity_name: ts.EntityName): string {
452+
private getEntityNameRoot(entity_name: ts.EntityName): string {
418453
if (ts.isIdentifier(entity_name)) {
419454
return entity_name.text;
420455
} else {
421-
return this.getEntityNameText(entity_name.left) + '.' + entity_name.right.text;
456+
return this.getEntityNameRoot(entity_name.left);
422457
}
423458
}
424459
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
///<reference types="svelte" />
2+
;;
3+
interface A {
4+
type: string;
5+
};;
6+
7+
interface Props extends A {
8+
a: string;
9+
};function $$render() {
10+
11+
12+
13+
const { }: Props = $props();
14+
;
15+
async () => {};
16+
return { props: {} as any as Props, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
17+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
18+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
19+
export default Input__SvelteComponent_;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script lang="ts">
2+
interface A {
3+
type: string;
4+
}
5+
6+
interface Props extends A {
7+
a: string;
8+
}
9+
const { }: Props = $props();
10+
</script>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
///<reference types="svelte" />
2+
;;
3+
type Props = {
4+
data: {cfg: string};
5+
};;function $$render() {
6+
7+
8+
let { data }: Props = $props();
9+
10+
type A = typeof data.cfg;
11+
type B = (typeof data)['cfg'];
12+
;
13+
async () => {};
14+
return { props: {} as any as Props, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
15+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
16+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
17+
export default Input__SvelteComponent_;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script lang="ts">
2+
type Props = {
3+
data: {cfg: string};
4+
};
5+
let { data }: Props = $props();
6+
7+
type A = typeof data.cfg;
8+
type B = (typeof data)['cfg'];
9+
</script>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
///<reference types="svelte" />
2+
;function $$render() {
3+
4+
const a = 1;
5+
6+
interface A {
7+
Abc: typeof a
8+
}
9+
10+
interface Abc {
11+
foo: A.Abc
12+
}
13+
14+
let {}: Abc = $props();
15+
;
16+
async () => {};
17+
return { props: {} as any as Abc, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
18+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
19+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
20+
export default Input__SvelteComponent_;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script lang="ts">
2+
const a = 1;
3+
4+
interface A {
5+
Abc: typeof a
6+
}
7+
8+
interface Abc {
9+
foo: A.Abc
10+
}
11+
12+
let {}: Abc = $props();
13+
</script>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
///<reference types="svelte" />
2+
;function $$render() {
3+
4+
const a = 1;
5+
6+
namespace A {
7+
export type Abc = typeof a
8+
}
9+
10+
interface Abc {
11+
foo: A.Abc
12+
}
13+
14+
let {}: Abc = $props();
15+
;
16+
async () => {};
17+
return { props: {} as any as Abc, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
18+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
19+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
20+
export default Input__SvelteComponent_;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script lang="ts">
2+
const a = 1;
3+
4+
namespace A {
5+
export type Abc = typeof a
6+
}
7+
8+
interface Abc {
9+
foo: A.Abc
10+
}
11+
12+
let {}: Abc = $props();
13+
</script>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
///<reference types="svelte" />
2+
;
3+
namespace A {
4+
export type Abd = number
5+
}
6+
;;function $$render() {
7+
8+
interface A {
9+
Abc: number
10+
}
11+
12+
let {Abc}: A = $props()
13+
;
14+
async () => {
15+
16+
};
17+
return { props: {} as any as A, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }}
18+
const Input__SvelteComponent_ = __sveltets_2_fn_component($$render());
19+
type Input__SvelteComponent_ = ReturnType<typeof Input__SvelteComponent_>;
20+
export default Input__SvelteComponent_;

0 commit comments

Comments
 (0)