/** * # 模块 Elementor 文档瘦身 * WordPress 导入的 Elementor JSON 包含大量桌面/平板/移动端响应式字段与空值, * 整份文档会随 TanStack Router 脱水序列化进 SSR HTML(首页曾达 391KB), * 严重影响 SEO 与首字节体积。本模块只保留渲染器实际读取的字段。 */ /** # 成员变量 渲染器(Renderer.tsx)实际读取的 settings 字段白名单 **/ const SETTING_KEYS = new Set([ // 通用布局/背景/边框 "__globals__", "align", "background_background", "background_color", "background_color_b", "background_color_stop_b", "background_image", // section 级背景定位/平铺(H11 底图 bottom center + no-repeat) "background_position", "background_repeat", // 背景尺寸(initial → 100% auto,H13 列背景楼体图) "background_size", // icon-list 视图(inline = 横排链接条,H13) "view", "border_border", "border_color", "border_radius", "border_width", "content_width", "custom_height", "padding", "layout", "height", "size", "space", "color", "text_color", "title_color", "button_text_color", "button_background_hover_color", "hover_color", "text_padding", // image-box 系列:图定位(position 默认 top)与标题标签(title_size 默认 h3) "position", "title_size", // image-box 行布局(H11):图宽 image_size%、标题底距、内容垂直对齐 "image_size", "title_bottom_space", "content_vertical_alignment", // image-box 图标间距/文本对齐/标题与描述排版(H13.5 counter 条) "image_space", "text_align", "title_typography_typography", "title_typography_font_size", "title_typography_font_size_mobile", "title_typography_font_size_tablet", "title_typography_font_weight", "description_typography_typography", "description_typography_font_size", "description_typography_font_size_mobile", "description_typography_font_size_tablet", "gradient_angle", "color_a", "color_b", "header_size", // 组件自带盒间距与列 margin(heading/text-editor 的 _padding;内层列 margin) "_padding", "margin", // Typography 组合字段(typography_typography=custom 时逐键采用) "typography_typography", "typography_font_family", "typography_font_size", "typography_font_size_tablet", "typography_font_size_mobile", "typography_font_weight", "typography_line_height", "typography_letter_spacing", "typography_word_spacing", "typography_text_transform", "typography_font_style", "typography_text_decoration", // 列结构 "_column_size", "_inline_size", // section 间隙档位(决定子列默认 populated padding:no 0 / default 10 / …) "gap", // 各 widget 数据 "heading_title", "title", "title_text", "description", "description_text", "selected_icon", "url", "width", "text", "editor", "html", "source_code", "image", "link", "prefix", "suffix", "ending_number", "icon_list", "social_icon_list", "slides", "slides_height", "tabs", "tabs_align_horizontal", "tab_active_color", "video_url", "youtube_url", "map", "posts_per_page", "cards_posts_per_page", "post_type", "eael_ticker_tag_text", // EAEL Infobox / flip-box / 高级标签页 / 数据表 / 评价等(Renderer 对应组件读取) "custom_css", "_css_classes", "eael_infobox_title", "eael_infobox_text", "eael_infobox_title_tag", "eael_infobox_image", "eael_infobox_img_type", "eael_infobox_img_margin", "eael_infobox_title_margin", "eael_infobox_title_margin_mobile", "eael_infobox_title_typography_typography", "eael_infobox_title_typography_font_size", "hover_animation", // widget 高级盒模型(如 eael-info-box 边框卡:_border_* + _padding/_margin) "_margin", // widget 宽度模式(auto → 按内容宽横排,如 H7 图标行) "_element_width", // widget 容器背景(如 H4 标题下的橙线图) "_background_image", "_background_position", "_background_repeat", // section 背景遮罩(.elementor-background-overlay,H11 电路板图+渐变) "background_overlay_background", "background_overlay_color", "background_overlay_color_b", "background_overlay_opacity", "background_overlay_gradient_angle", "_border_border", "_border_color", "_border_width", "_border_radius", "title_text_a", "description_text_a", "button_text", "background_a_image", "eael_adv_tabs_tab", "eael_data_table_header_cols_data", "eael_data_table_content_rows", "eael_testimonial_name", "eael_testimonial_company_title", "eael_testimonial_description", ]); /** # 函数 收窄对象型 settings 值(渲染器只读 url/alt/target 等少数字段) * 参数 key settings 键名;val 原始值 * 返回值 收窄后的值;非命中键原样返回 */ function slimSettingValue(key: string, val: unknown): unknown { if (!val || typeof val !== "object" || Array.isArray(val)) return val; const v = val as Record; if (key === "image" || key === "background_image" || key === "background_a_image" || key === "eael_infobox_image") { // 渲染器只读 url / alt return { url: v.url, alt: v.alt }; } if (key === "link") { // 渲染器只读 url / target / is_external(is_external=on → target=_blank,与原站 link_attributes 一致) return { url: v.url, target: v.target, is_external: v.is_external }; } return val; } /** # 函数 递归瘦身单个 Elementor 元素 * 参数 el 原始元素(section/column/widget 通用结构) * 返回值 仅含 id/elType/widgetType/isInner/白名单 settings/elements 的新对象 */ function slimElement(el: unknown): unknown { if (!el || typeof el !== "object") return el; const src = el as Record; const out: Record = {}; if (src.id != null) out.id = src.id; if (src.elType != null) out.elType = src.elType; if (src.widgetType != null) out.widgetType = src.widgetType; if (src.isInner) out.isInner = true; const st = src.settings; if (st && typeof st === "object" && !Array.isArray(st)) { const slim: Record = {}; for (const key of Object.keys(st as Record)) { if (SETTING_KEYS.has(key)) slim[key] = slimSettingValue(key, (st as Record)[key]); } if (Object.keys(slim).length > 0) out.settings = slim; } if (Array.isArray(src.elements) && src.elements.length > 0) { out.elements = src.elements.map(slimElement); } return out; } /** # 函数 瘦身整份 Elementor 文档(数组或 {elements:[]} 包装均支持) * 参数 doc 原始文档(来自 meta.elementor) * 返回值 瘦身后的文档;非对象/数组输入原样返回 */ export function slimElementorDoc(doc: unknown): unknown { if (Array.isArray(doc)) return doc.map(slimElement); if (doc && typeof doc === "object" && Array.isArray((doc as Record).elements)) { const wrapped = doc as Record; return { elements: (wrapped.elements as unknown[]).map(slimElement) }; } return doc; } /** # 函数 瘦身内容 meta:替换 meta.elementor 为瘦身版(原 meta 其余字段保留) * 参数 meta 原始 meta JSON(可能为 null) * 返回值 elementor 已瘦身的 meta;无 elementor 时原样返回 */ export function slimMetaElementor(meta: T): T { if (!meta || typeof meta !== "object") return meta; const m = meta as Record; if (!m.elementor) return meta; return { ...m, elementor: slimElementorDoc(m.elementor) } as T; }