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

Code before:
				node.expression,
				{},
				{
					Identifier(id_node, { path, next }) {
						const parent = path[path.length - 1];
						if (id_node.name !== '$$props' && id_node.name !== '$$restProps') return;

						if (parent.type === 'MemberExpression' && parent.object !== id_node) return;

						const props_name =
							id_node.name === '$$props' || state.analysis.uses_props
								? state.names.props
								: state.names.rest;
						const props_start =
							/** @type {number} */ (id_node.start) - /** @type {number} */ (node.expression.start);

						expression =
							expression.substring(0, props_start + difference) +
							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,

Review comment (refactor):
Is it possible to simplify this by calling `next` on `SvelteComponent` first, thereby transforming `$$props` etc through the other visitors, and then do a `str.move(..)` of the `this` binding to the correct position (to the `$derived` or `@const`)?

Code after:
		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' ||
					part.type === 'SvelteComponent'
				) {
					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);