function create_node_analyser({ resolve, static_exports = new Map() }) {
	/**
	 * Computes the final page options for a node (if possible). Otherwise, returns `null`.
	 * @param {import('types').PageNode} node
	 * @returns {Promise<Record<string, any> | null>}
	 */
	const get_page_options = async (node) => {
		const key = node.universal || node.server;
		if (key && static_exports.has(key)) {
			return { ...static_exports.get(key)?.page_options };
		}

		/** @type {Record<string, any>} */
		let page_options = {};

		if (node.parent) {
			const parent_options = await get_page_options(node.parent);

			const parent_key = node.parent.universal || node.parent.server;
			if (key && parent_key) {
				static_exports.get(parent_key)?.children.push(key);
			}

			if (parent_options === null) {
				// if the parent cannot be analysed, we can't know what page options
				// the child node inherits, so we also mark it as unanalysable
				if (key) {
					static_exports.set(key, { page_options: null, children: [] });
				}
				return null;
			}

			page_options = { ...parent_options };
		}

		if (node.server) {
			const module = await resolve(node.server);
			for (const page_option in inheritable_page_options) {
				if (page_option in module) {
					page_options[page_option] = module[page_option];
				}
			}
		}

		if (node.universal) {
			const input = read(node.universal);
			const universal_page_options = statically_analyse_page_options(node.universal, input);

			if (universal_page_options === null) {
				static_exports.set(node.universal, { page_options: null, children: [] });
				return null;
			}

			page_options = { ...page_options, ...universal_page_options };
		}

		if (key) {
			static_exports.set(key, { page_options, children: [] });
		}

		return page_options;
	};

	/**
	 * @param {string} file
	 */
	const invalidate_page_options = (file) => {
		static_exports.get(file)?.children.forEach((child) => static_exports.delete(child));
		static_exports.delete(file);
	};

	return {
		get_page_options,
		invalidate_page_options
	};
}
@param {{
  resolve: (file: string) => Promise<Record<string, any>>;
  static_exports?: Map<string, { page_options: Record<string, any> | null, children: string[] }>;
}} opts