put the code where it's used

pull/14315/head
Rich Harris 2 years ago
parent e9ecbcf6ca
commit 0b3e74fbc2

@ -1,8 +1,8 @@
/** @import { AST, Binding } from '#compiler' */ /** @import { AST, Binding, SvelteNode } from '#compiler' */
/** @import { Scope } from '../../scope' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import { validate_block_not_empty, validate_opening_tag } from './shared/utils.js'; import { validate_block_not_empty, validate_opening_tag } from './shared/utils.js';
import * as e from '../../../errors.js'; import * as e from '../../../errors.js';
import { can_hoist_snippet } from '../../3-transform/utils.js';
/** /**
* @param {AST.SnippetBlock} node * @param {AST.SnippetBlock} node
@ -76,3 +76,53 @@ export function SnippetBlock(node, context) {
} }
} }
} }
/**
* @param {AST.SnippetBlock} node
* @param {Map<SvelteNode, Scope>} scopes
* @param {Scope} scope
*/
function can_hoist_snippet(node, scope, scopes, visited = new Set()) {
let can_hoist = true;
ref_loop: for (const [reference] of scope.references) {
const local_binding = scope.get(reference);
if (local_binding) {
if (local_binding.node === node.expression || local_binding.scope.function_depth === 0) {
continue;
}
/** @type {Scope | null} */
let current_scope = local_binding.scope;
while (current_scope !== null) {
if (current_scope === scope) {
continue ref_loop;
}
current_scope = current_scope.parent;
}
// Recursively check if another snippet can be hoisted
if (local_binding.kind === 'normal') {
for (const ref of local_binding.references) {
const parent = ref.path.at(-1);
if (ref.node === local_binding.node && parent?.type === 'SnippetBlock') {
const ref_scope = scopes.get(parent);
if (visited.has(ref)) {
break;
}
visited.add(ref);
if (ref_scope && can_hoist_snippet(parent, ref_scope, scopes, visited)) {
continue ref_loop;
}
break;
}
}
}
can_hoist = false;
break;
}
}
return can_hoist;
}

@ -453,53 +453,3 @@ export function transform_inspect_rune(node, context) {
return b.call('$.inspect', as_fn ? b.thunk(b.array(arg)) : b.array(arg)); return b.call('$.inspect', as_fn ? b.thunk(b.array(arg)) : b.array(arg));
} }
} }
/**
* @param {AST.SnippetBlock} node
* @param {Map<SvelteNode, Scope>} scopes
* @param {Scope} scope
*/
export function can_hoist_snippet(node, scope, scopes, visited = new Set()) {
let can_hoist = true;
ref_loop: for (const [reference] of scope.references) {
const local_binding = scope.get(reference);
if (local_binding) {
if (local_binding.node === node.expression || local_binding.scope.function_depth === 0) {
continue;
}
/** @type {Scope | null} */
let current_scope = local_binding.scope;
while (current_scope !== null) {
if (current_scope === scope) {
continue ref_loop;
}
current_scope = current_scope.parent;
}
// Recursively check if another snippet can be hoisted
if (local_binding.kind === 'normal') {
for (const ref of local_binding.references) {
const parent = ref.path.at(-1);
if (ref.node === local_binding.node && parent?.type === 'SnippetBlock') {
const ref_scope = scopes.get(parent);
if (visited.has(ref)) {
break;
}
visited.add(ref);
if (ref_scope && can_hoist_snippet(parent, ref_scope, scopes, visited)) {
continue ref_loop;
}
break;
}
}
}
can_hoist = false;
break;
}
}
return can_hoist;
}

Loading…
Cancel
Save