Elementor Atomic Builder 内部 CSS Converter 扩展实战:从属性转换器、简写展开器到 CI 覆盖机制

Elementor Atomic Builder 内部 CSS Converter 扩展实战:从属性转换器、简写展开器到 CI 覆盖机制 Elementor Atomic Builder 内部 CSS Converter 扩展实战从属性转换器、简写展开器到 CI 覆盖机制【免费下载链接】elementorThe most advanced frontend drag drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor本文基于 Elementor 仓库中docs/atomic-builder/examples/internal-extend-css-converter.md的官方示例文档展开讲解 Atomic Builder原子组件架构导入/迁移阶段使用的 CSS Converter 如何扩展如何编写Property_Converter_Base子类把传统 CSS 声明映射为原子化Style_Schema的 PropValue如何编写Shorthand_Expander_Base子类把border、background等简写展开为长写属性以及如何满足covered_properties()覆盖集与 CI 覆盖率测试。读完本文你可以完整走通新增一个 CSS 属性转换能力在 Elementor 核心仓库中的落地路径并理解为什么该扩展点只面向核心团队无公开 WordPress 发现钩子。定位Converter 在导入/迁移链路中的位置CSS Converter 的核心职责是在导入/迁移时把遗留的 CSS 声明映射为原子化Style_Schema的 PropValue而不是渲染阶段参与样式生成的 styles transformers两者不是同一机制。它服务于把自由 CSS 文本转成结构化 props 的场景产物是三元组{ props, customCss, rejected }——能结构化表达的进props表达不了的落回customCss结构性不兼容的如animation*依赖无法内联声明的keyframes进rejected以便客户端向 LLM 提示不支持。从源码看整个管道在 Css_Converter::convert() 中串起$rules $this-dedupe( $this-expand_shorthands( $this-parse( $css ) ) );即parse()→expand_shorthands()简写展开器在此运行→dedupe()同一属性后值覆盖前值→ 逐条规则走try_convert()属性转换器在此运行未认领的规则拼回customCss→ 变量转换与Props_Parser校验 →cleanup_props()全 null 的对象折叠为顶层null。扩展点expander / converter就挂在这条管道的两个环节上。该模块的边界很明确扩展方式是核心内的工厂注册不存在公开 WordPress filter 用于发现 converter 或 expander。示例文档给出的结论是——Relevant for Core only第三方插件要么提核心 PR要么自建私有Css_Converter实例。配套文档可参见 css-converter/extension.md、pipeline 文档 以及对应的技能说明 .cursor/skills/internal-extend-css-converter/SKILL.md。编写属性转换器以Size_Property_Converter为参照转换器统一继承 Property_Converter_Base它是模板方法模式的骨架子类需要实现的抽象方法只有两个方法签名作用get_supported_properties()protected function get_supported_properties(): array声明本转换器精确拥有的属性名列表do_convert()protected function do_convert( Conversion_Context $context, array $rule ): bool真正的长写声明 → PropValue转换基类的分发逻辑在convert()中源码 L27-L39先调用get_custom_converter()钩出特殊规则返回null则走默认路径随后若$rule[value]为null则走convert_null()默认直接把属性置null并返回true否则走do_convert()。返回值语义返回true表示本转换器认领了该规则返回false表示放弃交由下一个转换器尝试。官方示例文档给出的参照实现针对单一 Size 类属性?php namespace Elementor\Modules\AtomicWidgets\CssConverter\Converters; use Elementor\Modules\AtomicWidgets\CssConverter\Conversion_Context; use Elementor\Modules\AtomicWidgets\CssConverter\Property_Converter_Base; use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type; class My_Property_Converter extends Property_Converter_Base { private string $property; public function __construct( string $property ) { $this-property $property; } protected function get_supported_properties(): array { return [ $this-property ]; } protected function do_convert( Conversion_Context $context, array $rule ): bool { $context-set_prop( $this-property, Size_Prop_Type::generate( [ size 10, unit px, ] ) ); return true; } }实际仓库中这类叶子转换器的成规模用法见 size-property-converter.php、color-property-converter.php。注册Converter_Registry_Factory::real_converters()新转换器注册到 Converter_Registry_Factory::real_converters()私有方法位于工厂类同一文件内。该方法的组织方式是按属性家族批量循环 个别属性显式挂接foreach ( self::SIZE_PROPERTIES as $property )统一挂Size_Property_Converter并把UNITLESS_SIZE_PROPERTIES目前为line-height标记为允许无单位值无单位时保留customunit 原样渲染STRING_PROPERTIES统一挂String_Property_Converter且枚举白名单直接从活的Style_Schema的get_enum()取值避免与 schema 漂移border-radius、flex、box-shadow等独有属性的定制转换器则以$converters[ border-radius ] new ...的形式逐一显式注册。外层的公开入口 create() 在注册完真实转换器后还做两件事为每个REJECTED_PROPERTIESanimation及其 8 个长写挂Rejected_Converter再对covered_properties()中没有真实转换器覆盖的每个属性挂Noop_Converter认领该属性但拒绝转换使其落入customCss。这保证了每个属性恰好一个转换器的不变量。编写简写展开器Shorthand Expander简写展开器运行在转换器之前负责把border: 1px solid #ccc这类声明原地改写为长写规则保留源码级联顺序。它继承 Shorthand_Expander_BaseAPI 为protected get_supported_properties(): array——声明拥有的简写属性名protected do_expand( array $rule ): array——非 null 值时的展开逻辑返回长写规则数组可选expand_null()——null 重置的扇出fan-out。基类expand()的分发是value为null走expand_null()否则走do_expand()默认的expand_null()会把同一属性以null值重新发出覆盖简单改名器场景。注册位置是 Expander_Registry_Factory::create()顺序至关重要——展开器按注册顺序首匹配生效first match wins。当前核心的注册顺序为Physical_To_Logical_Expandertop/right/bottom/left→ 逻辑 inset 长写→Background_Shorthand_Expanderbackground→ color/image 等长写→Outline_Shorthand_Expanderoutline→ width/style/color/offset→Border_Shorthand_Expanderborder及四个border-{side}→ 各边的 width/style/color其中逐边的 style/color 没有转换器会路由到customCss。Css_Converter中展开环节expand_shorthands() 附近还有一个稳健性设计没有匹配到展开器、展开器拒绝空结果或抛异常时规则原样保留继续进入转换器循环并享受custom_css兜底——这正是NOOP_PROPERTIES里保留background条目的原因展开器无法分解的奇异语法最终由该 Noop 把原始声明留在customCss中。覆盖集与 CI 覆盖率测试Converter_Registry_Factory用一组家族常量把所有已声明覆盖的属性硬编码出来STRING_PROPERTIES、SIZE_PROPERTIES、NUMBER_PROPERTIES、COLOR_PROPERTIES、SPAN_PROPERTIES、DIMENSIONS_PROPERTIES、FILTER_PROPERTIES、OTHER_PROPERTIES外加NOOP_PROPERTIES有意按设计保留在customCss的属性如stroke*SVG 家族与REJECTED_PROPERTIES。covered_properties() 把它们array_merge成唯一的覆盖清单。CI 侧的护栏是 test-css-converter-rest-api.php 中的test_coverage__every_style_schema_property_is_hardcoded_as_covered它取Style_Schema::get_style_schema()的全部键与Converter_Registry_Factory::covered_properties()做差集任何Style_Schema键未出现在覆盖集中都会让 CI 失败错误信息直接列出未覆盖属性名。这个测试刻意与活的 schema 对比而不是从 schema 反推覆盖集目的就是往 schema 加属性却不补覆盖时PR 在 CI 阶段就会被拦下——扩展一个新属性转换器后跑覆盖率测试不是可选步骤而是必过关卡。一个容易踩的边界侧长写padding-top、border-top-left-radius等通过Object_Side_Merge_Converter/Object_Field_Merge_Converter累积进聚合对象如padding-top→padding.block-start它们本身不是顶层Style_Schema键因此不总是covered_properties()的顶层条目——对应边长写在DIMENSIONS_SIDE_SPECS与border_side_specs()中维护并由covered_properties()的array_keys( ... )一并纳入。同理background-color→background.color走的是Object_Field_Merge_Converter的字段合并模式见 real_converters() 中的 background 段。null / 重置语义在各阶段的行为扩展转换器时必须理解 null 在管道各阶段的传播与 extension.md 一致并可在源码中逐一印证阶段null 行为源码依据parse()字符串null归一为 PHPnullcss-converter.phpdedupe()同一属性后出现的值生效later value winsdedupe()展开器expand_null()把重置扇出到各长写shorthand-expander-base.php转换器convert_null()把属性或字段置nullproperty-converter-base.phpcleanup_props()对象的所有子值全为 null 时折叠为顶层nullcleanup_props()测试侧有直接验证test_coverage 同文件中的 null 重置用例 断言margin: null使四边全部 null 后props.margin折叠为null且customCss为空。扩展落地路径与相邻机制示例文档给出两条公开路径源码均能对上核心 PR向modules/atomic-widgets/css-converter/下的工厂类Converter_Registry_Factory/Expander_Registry_Factory提交 expander 或 converter并同步更新覆盖常量与测试插件内部工具自建Css_Converter实例——从 构造函数 可见其Converter_Registry、Conversion_Failure_Reporter、Expander_Registry、Variable_Prop_Value_Transformer均为依赖注入可手工装配注册表但这条路不接入核心的导入 UI。需要澄清一个常见的误认elementor/atomic-widgets/styles/schemafilter在 style-schema.php 经apply_filters应用variables/hooks.php 中有实际订阅方只用于扩展样式 schema 本身并不注册任何 converter 或 expander——schema 扩展与转换器发现是两条独立的通道不要指望挂 filter 就能让新属性被转换。小结扩展 Elementor Atomic Builder 的 CSS Converter 遵循固定的三步曲在converters/或expanders/下继承Property_Converter_Base或Shorthand_Expander_Base实现声明式接口在对应工厂方法中完成注册expander 注意注册顺序最后把新属性补进覆盖常量并通过test-css-converter-rest-api.php的覆盖率测试。整个过程没有公开 WordPress 钩子是典型的核心内部扩展点第三方需求只能通过核心 PR 或私有实例化两条路实现。【免费下载链接】elementorThe most advanced frontend drag drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考