function list_features(route, manifest_data, server_manifest, tracked_features) {
	const features = new Set();

	const route_data = /** @type {import('types').RouteData} */ (
		manifest_data.routes.find((r) => r.id === route.id)
	);

	/** @param {string} id */
	function visit(id) {
		const chunk = server_manifest[id];
		if (!chunk) return;

		if (chunk.file in tracked_features) {
			for (const feature of tracked_features[chunk.file]) {
				features.add(feature);
			}
		}

		if (chunk.imports) {
			for (const id of chunk.imports) {
				visit(id);
			}
		}
	}

	let page_node = route_data?.leaf;
	while (page_node) {
		if (page_node.server) visit(page_node.server);
		page_node = page_node.parent ?? null;
	}

	if (route_data.endpoint) {
		visit(route_data.endpoint.file);
	}

	if (manifest_data.hooks.server) {
		// TODO if hooks.server.js imports `read`, it will be in the entry chunk
		// we don't currently account for that case
		visit(manifest_data.hooks.server);
	}

	return Array.from(features);
}
@param {import('types').SSRRoute} route
@param {import('types').ManifestData} manifest_data
@param {import('vite').Manifest} server_manifest
@param {Record<string, string[]>} tracked_features