diff --git a/packages/svelte2tsx/src/svelte2tsx/index.ts b/packages/svelte2tsx/src/svelte2tsx/index.ts index 68a688bef..b5e2a9e65 100644 --- a/packages/svelte2tsx/src/svelte2tsx/index.ts +++ b/packages/svelte2tsx/src/svelte2tsx/index.ts @@ -166,11 +166,13 @@ export function svelte2tsx( } if (moduleScriptTag || scriptTag) { - const allowed = exportedNames.hoistableInterfaces.getAllowedValues(); for (const [start, end, globals] of rootSnippets) { const hoist_to_module = moduleScriptTag && - (globals.size === 0 || [...globals.keys()].every((id) => allowed.has(id))); + (globals.size === 0 || + [...globals.keys()].every((id) => + exportedNames.hoistableInterfaces.isAllowedReference(id) + )); if (hoist_to_module) { str.move( diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts index 7ee4de9b9..bca4a25ce 100644 --- a/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts +++ b/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts @@ -5,8 +5,9 @@ import MagicString from 'magic-string'; * Collects all imports and module-level declarations to then find out which interfaces/types are hoistable. */ export class HoistableInterfaces { - private import_value_set: Set = new Set(); - private import_type_set: Set = new Set(); + private module_types: Set = new Set(); + private disallowed_types = new Set(); + private disallowed_values = new Set(); private interface_map: Map< string, { type_deps: Set; value_deps: Set; node: ts.Node } @@ -18,6 +19,7 @@ export class HoistableInterfaces { value_deps: new Set() }; + /** should be called before analyzeInstanceScriptNode */ analyzeModuleScriptNode(node: ts.Node) { // Handle Import Declarations if (ts.isImportDeclaration(node) && node.importClause) { @@ -30,9 +32,7 @@ export class HoistableInterfaces { node.importClause.namedBindings.elements.forEach((element) => { const import_name = element.name.text; if (is_type_only || element.isTypeOnly) { - this.import_type_set.add(import_name); - } else { - this.import_value_set.add(import_name); + this.module_types.add(import_name); } }); } @@ -41,9 +41,7 @@ export class HoistableInterfaces { if (node.importClause.name) { const default_import = node.importClause.name.text; if (is_type_only) { - this.import_type_set.add(default_import); - } else { - this.import_value_set.add(default_import); + this.module_types.add(default_import); } } @@ -54,40 +52,17 @@ export class HoistableInterfaces { ) { const namespace_import = node.importClause.namedBindings.name.text; if (is_type_only) { - this.import_type_set.add(namespace_import); - } else { - this.import_value_set.add(namespace_import); + this.module_types.add(namespace_import); } } } - // Handle top-level declarations - if (ts.isVariableStatement(node)) { - node.declarationList.declarations.forEach((declaration) => { - if (ts.isIdentifier(declaration.name)) { - this.import_value_set.add(declaration.name.text); - } - }); - } - - if (ts.isFunctionDeclaration(node) && node.name) { - this.import_value_set.add(node.name.text); - } - - if (ts.isClassDeclaration(node) && node.name) { - this.import_value_set.add(node.name.text); - } - - if (ts.isEnumDeclaration(node)) { - this.import_value_set.add(node.name.text); - } - if (ts.isTypeAliasDeclaration(node)) { - this.import_type_set.add(node.name.text); + this.module_types.add(node.name.text); } if (ts.isInterfaceDeclaration(node)) { - this.import_type_set.add(node.name.text); + this.module_types.add(node.name.text); } } @@ -103,9 +78,7 @@ export class HoistableInterfaces { node.importClause.namedBindings.elements.forEach((element) => { const import_name = element.name.text; if (is_type_only) { - this.import_type_set.add(import_name); - } else { - this.import_value_set.add(import_name); + this.module_types.add(import_name); } }); } @@ -114,9 +87,7 @@ export class HoistableInterfaces { if (node.importClause.name) { const default_import = node.importClause.name.text; if (is_type_only) { - this.import_type_set.add(default_import); - } else { - this.import_value_set.add(default_import); + this.module_types.add(default_import); } } @@ -127,9 +98,7 @@ export class HoistableInterfaces { ) { const namespace_import = node.importClause.namedBindings.name.text; if (is_type_only) { - this.import_type_set.add(namespace_import); - } else { - this.import_value_set.add(namespace_import); + this.module_types.add(namespace_import); } } } @@ -167,9 +136,9 @@ export class HoistableInterfaces { } }); - if (this.import_type_set.has(interface_name)) { - // shadowed; delete because we can't hoist - this.import_type_set.delete(interface_name); + if (this.module_types.has(interface_name)) { + // shadowed; we can't hoist + this.disallowed_types.add(interface_name); } else { this.interface_map.set(interface_name, { type_deps: type_dependencies, @@ -193,9 +162,9 @@ export class HoistableInterfaces { generics ); - if (this.import_type_set.has(alias_name)) { - // shadowed; delete because we can't hoist - this.import_type_set.delete(alias_name); + if (this.module_types.has(alias_name)) { + // shadowed; we can't hoist + this.disallowed_types.add(alias_name); } else { this.interface_map.set(alias_name, { type_deps: type_dependencies, @@ -209,29 +178,21 @@ export class HoistableInterfaces { if (ts.isVariableStatement(node)) { node.declarationList.declarations.forEach((declaration) => { if (ts.isIdentifier(declaration.name)) { - this.import_value_set.delete(declaration.name.text); + this.disallowed_values.add(declaration.name.text); } }); } if (ts.isFunctionDeclaration(node) && node.name) { - this.import_value_set.delete(node.name.text); + this.disallowed_values.add(node.name.text); } if (ts.isClassDeclaration(node) && node.name) { - this.import_value_set.delete(node.name.text); + this.disallowed_values.add(node.name.text); } if (ts.isEnumDeclaration(node)) { - this.import_value_set.delete(node.name.text); - } - - if (ts.isTypeAliasDeclaration(node)) { - this.import_type_set.delete(node.name.text); - } - - if (ts.isInterfaceDeclaration(node)) { - this.import_type_set.delete(node.name.text); + this.disallowed_values.add(node.name.text); } } @@ -281,13 +242,26 @@ export class HoistableInterfaces { continue; } - const can_hoist = [...deps.type_deps, ...deps.value_deps].every((dep) => { - return ( - this.import_type_set.has(dep) || - this.import_value_set.has(dep) || - hoistable_interfaces.has(dep) - ); - }); + let can_hoist = true; + + for (const dep of deps.type_deps) { + if (this.disallowed_types.has(dep)) { + this.disallowed_types.add(interface_name); + can_hoist = false; + break; + } + if (this.interface_map.has(dep) && !hoistable_interfaces.has(dep)) { + can_hoist = false; + } + } + + for (const dep of deps.value_deps) { + if (this.disallowed_values.has(dep)) { + this.disallowed_types.add(interface_name); + can_hoist = false; + break; + } + } if (can_hoist) { hoistable_interfaces.set(interface_name, deps.node); @@ -301,11 +275,7 @@ export class HoistableInterfaces { ...this.props_interface.type_deps, ...this.props_interface.value_deps ].every((dep) => { - return ( - this.import_type_set.has(dep) || - this.import_value_set.has(dep) || - hoistable_interfaces.has(dep) - ); + return !this.disallowed_types.has(dep) && !this.disallowed_values.has(dep); }); if (can_hoist) { @@ -328,7 +298,7 @@ export class HoistableInterfaces { if (!this.props_interface.name) return; for (const generic of generics) { - this.import_type_set.delete(generic); + this.disallowed_types.add(generic); } const hoistable = this.determineHoistableInterfaces(); @@ -362,8 +332,8 @@ export class HoistableInterfaces { } } - getAllowedValues() { - return this.import_value_set; + isAllowedReference(reference: string) { + return !this.disallowed_values.has(reference); } /** diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts index 69587fe79..0c2610728 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts @@ -18,6 +18,11 @@ import { imported } from './x'; { svelteHTML.createElement("div", {});module; } };return __sveltets_2_any(0)}; const hoistable7/*Ωignore_positionΩ*/ = ()/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { { svelteHTML.createElement("div", {});imported; } +};return __sveltets_2_any(0)}; const hoistable8/*Ωignore_positionΩ*/ = ()/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { + { svelteHTML.createElement("div", {});global; } +};return __sveltets_2_any(0)}; const hoistable9/*Ωignore_positionΩ*/ = (props: HTMLAttributes)/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { };return __sveltets_2_any(0)}; const hoistable10/*Ωignore_positionΩ*/ = (foo)/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { + const bar = foo; + bar; };return __sveltets_2_any(0)};function render() { const not_hoistable/*Ωignore_positionΩ*/ = ()/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { { svelteHTML.createElement("div", {});foo; } @@ -40,6 +45,12 @@ async () => { + + + + + + diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte index 924dc61aa..fd7c1c4fd 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte @@ -35,6 +35,19 @@
{imported}
{/snippet} +{#snippet hoistable8()} +
{global}
+{/snippet} + +{#snippet hoistable9(props: HTMLAttributes)} + Referencing global types +{/snippet} + +{#snippet hoistable10(foo)} + {@const bar = foo} + {bar} +{/snippet} + {#snippet not_hoistable()}
{foo}
{/snippet}