function process_node(node, outdir, is_page, proxies, all_pages_have_load = true) {
	const params = `${is_page ? 'Route' : 'Layout'}Params`;
	const prefix = is_page ? 'Page' : 'Layout';

	const route_id = is_page ? 'RouteId' : 'LayoutRouteId';

	/** @type {string[]} */
	const declarations = [];
	/** @type {string[]} */
	const exports = [];

	/** @type {string} */
	let server_data;
	/** @type {string} */
	let data;

	ensureProxies(node, proxies);

	if (node.server) {
		const basename = path.basename(node.server);
		const proxy = proxies.server;
		if (proxy?.modified) {
			fs.writeFileSync(`${outdir}/proxy${basename}`, proxy.code);
		}

		server_data = get_data_type(node.server, 'null', proxy, true);

		const parent_type = `${prefix}ServerParentData`;

		declarations.push(`type ${parent_type} = ${get_parent_type(node, 'LayoutServerData')};`);

		// +page.js load present -> server can return all-optional data
		const output_data_shape =
			node.universal || (!is_page && all_pages_have_load)
				? 'Partial<App.PageData> & Record<string, any> | void'
				: `OutputDataShape<${parent_type}>`;
		exports.push(
			`export type ${prefix}ServerLoad<OutputData extends ${output_data_shape} = ${output_data_shape}> = Kit.ServerLoad<${params}, ${parent_type}, OutputData, ${route_id}>;`
		);

		exports.push(`export type ${prefix}ServerLoadEvent = Parameters<${prefix}ServerLoad>[0];`);

		if (is_page) {
			let type = 'unknown';
			if (proxy && proxy.exports.includes('actions')) {
				// If the file wasn't tweaked, we can use the return type of the original file.
				// The advantage is that type updates are reflected without saving.
				const from = proxy.modified
					? `./proxy${replace_ext_with_js(basename)}`
					: path_to_original(outdir, node.server);

				exports.push(
					'type ExcludeActionFailure<T> = T extends Kit.ActionFailure<any> ? never : T extends void ? never : T;',
					'type ActionsSuccess<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: ExcludeActionFailure<Awaited<ReturnType<T[Key]>>>; }[keyof T];',
					'type ExtractActionFailure<T> = T extends Kit.ActionFailure<infer X>	? X extends void ? never : X : never;',
					'type ActionsFailure<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: Exclude<ExtractActionFailure<Awaited<ReturnType<T[Key]>>>, void>; }[keyof T];',
					`type ActionsExport = typeof import('${from}').actions`,
					'export type SubmitFunction = Kit.SubmitFunction<Expand<ActionsSuccess<ActionsExport>>, Expand<ActionsFailure<ActionsExport>>>'
				);

				type = 'Expand<Kit.AwaitedActions<ActionsExport>> | null';
			}
			exports.push(`export type ActionData = ${type};`);
		}
	} else {
		server_data = 'null';
	}
	exports.push(`export type ${prefix}ServerData = ${server_data};`);

	const parent_type = `${prefix}ParentData`;
	declarations.push(`type ${parent_type} = ${get_parent_type(node, 'LayoutData')};`);

	if (node.universal) {
		const proxy = proxies.universal;
		if (proxy?.modified) {
			fs.writeFileSync(`${outdir}/proxy${path.basename(node.universal)}`, proxy.code);
		}

		const type = get_data_type(
			node.universal,
			`${parent_type} & EnsureDefined<${prefix}ServerData>`,
			proxy
		);

		data = `Expand<Omit<${parent_type}, keyof ${type}> & OptionalUnion<EnsureDefined<${type}>>>`;

		const output_data_shape =
			!is_page && all_pages_have_load
				? 'Partial<App.PageData> & Record<string, any> | void'
				: `OutputDataShape<${parent_type}>`;
		exports.push(
			`export type ${prefix}Load<OutputData extends ${output_data_shape} = ${output_data_shape}> = Kit.Load<${params}, ${prefix}ServerData, ${parent_type}, OutputData, ${route_id}>;`
		);

		exports.push(`export type ${prefix}LoadEvent = Parameters<${prefix}Load>[0];`);
	} else if (server_data === 'null') {
		data = `Expand<${parent_type}>`;
	} else {
		data = `Expand<Omit<${parent_type}, keyof ${prefix}ServerData> & EnsureDefined<${prefix}ServerData>>`;
	}

	exports.push(`export type ${prefix}Data = ${data};`);

	return { declarations, exports, proxies };

	/**
	 * @param {string} file_path
	 * @param {string} fallback
	 * @param {Proxy} proxy
	 * @param {boolean} expand
	 */
	function get_data_type(file_path, fallback, proxy, expand = false) {
		if (proxy) {
			if (proxy.exports.includes('load')) {
				// If the file wasn't tweaked, we can use the return type of the original file.
				// The advantage is that type updates are reflected without saving.
				const from = proxy.modified
					? `./proxy${replace_ext_with_js(path.basename(file_path))}`
					: path_to_original(outdir, file_path);
				const type = `Kit.LoadProperties<Awaited<ReturnType<typeof import('${from}').load>>>`;
				return expand ? `Expand<OptionalUnion<EnsureDefined<${type}>>>` : type;
			} else {
				return fallback;
			}
		} else {
			return 'unknown';
		}
	}
}
@param {import('types').PageNode} node
@param {string} outdir
@param {boolean} is_page
@param {Proxies} proxies
@param {boolean} [all_pages_have_load]