Repository: sveltejs/svelte
File: packages/svelte/src/compiler/migrate/index.js (JavaScript)

Code before:
							props_name +
							expression.substring(props_start + id_node.name.length + difference);

						difference += props_name.length - id_node.name.length;
						next();
					}
				}
			);
		}

		if (
			(node.expression.type !== 'Identifier' && node.expression.type !== 'MemberExpression') ||
			!regex_valid_component_name.test(expression)
		) {
			let current_expression = expression;
			expression = state.scope.generate('SvelteComponent');
			let needs_derived = true;
			for (let i = path.length - 1; i >= 0; i--) {
				const part = path[i];
				if (
					part.type === 'EachBlock' ||
					part.type === 'AwaitBlock' ||
					part.type === 'IfBlock' ||
					part.type === 'KeyBlock' ||
					part.type === 'SnippetBlock' ||
					part.type === 'Component'
				) {
					const indent = state.str.original.substring(
						state.str.original.lastIndexOf('\n', node.start) + 1,
						node.start
					);
					state.str.prependLeft(
						node.start,
						`{@const ${expression} = ${current_expression}}\n${indent}`
					);
					needs_derived = false;
					continue;
				}
			}
			if (needs_derived) {
				if (state.derived_components.has(current_expression)) {
					expression = /** @type {string} */ (state.derived_components.get(current_expression));
				} else {
					state.derived_components.set(current_expression, expression);
				}
			}
		}

		state.str.overwrite(node.start + 1, node.start + node.name.length + 1, expression);

		if (state.str.original.substring(node.end - node.name.length - 1, node.end - 1) === node.name) {

Review comment (suggestion):
Also needs to take into account the "is a element with a named slot" case (e.g. `<div slot="foo" let:thisIsAComponent></div>`).

Code after:
					const indent = state.str.original.substring(
						state.str.original.lastIndexOf('\n', node.start) + 1,
						node.start
					);
					state.str.prependLeft(
						node.start,
						`{@const ${expression} = ${current_expression}}\n${indent}`
					);
					needs_derived = false;
					continue;
				}
			}
			if (needs_derived) {
				if (state.derived_components.has(current_expression)) {
					expression = /** @type {string} */ (state.derived_components.get(current_expression));
				} else {
					state.derived_components.set(current_expression, expression);
				}
			}
		}

		state.str.overwrite(node.start + 1, node.start + node.name.length + 1, expression);

		if (state.str.original.substring(node.end - node.name.length - 1, node.end - 1) === node.name) {
			state.str.overwrite(node.end - node.name.length - 1, node.end - 1, expression);
		}
		let this_pos = state.str.original.lastIndexOf('this', node.expression.start);
		while (!state.str.original.charAt(this_pos - 1).trim()) this_pos--;
		const end_pos = state.str.original.indexOf('}', node.expression.end) + 1;
		state.str.remove(this_pos, end_pos);
	},
	SvelteWindow(node, { state, next }) {
		handle_events(node, state);
		next();
	},
	SvelteBody(node, { state, next }) {
		handle_events(node, state);
		next();
	},
	SvelteDocument(node, { state, next }) {
		handle_events(node, state);
		next();
	},
	SlotElement(node, { state, next }) {
		if (state.analysis.custom_element) return;
		let name = 'children';
		let slot_name = 'default';
		let slot_props = '{ ';

		for (const attr of node.attributes) {
			if (attr.type === 'SpreadAttribute') {