资讯动态

Gutenberg 块序列化规范解析器(@wordpress/block-serialization-spec-parser)完全指南:从 PEG 文法到双端解析

发布时间:2026/9/17 3:20:10 来源:尧图企业网站定制
Gutenberg 块序列化规范解析器wordpress/block-serialization-spec-parser完全指南从 PEG 文法到双端解析【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg导读本文围绕 Gutenberg 项目中wordpress/block-serialization-spec-parser包展开它承载着 WordPress 块编辑器的“序列化规范”——用一份 PEGParsing Expression Grammar解析表达式文法文件描述块注释标记的合法语法并据此在构建期生成浏览器端 JavaScript 与 WordPress 服务端 PHP 两套解析器。读完本文你将理解块注释格式!-- wp:... --的完整文法规则、parse()的返回数据结构、双端生成的构建流程以及该包与wordpress/blocks、serializeRawBlock之间的上下游关系并能把示例直接跑起来验证。一、这个包解决什么问题在 Gutenberg 中一篇文档/一篇文章被序列化为“HTML 注释包裹的块标记”与普通 HTML 的混合体例如!-- wp:core/more --!--more--!-- /wp:core/more --解析器要做的是把这样的混合内容还原成结构化的块对象数组供编辑器恢复每个块的名称、属性与嵌套关系。而wordpress/block-serialization-spec-parser的特殊之处在于它不是手写解析逻辑而是先定义一份规范文法grammar.pegjs再用 PEG 解析器生成器产出真正的解析器。正如包 README 所述This library contains the grammar file (grammar.pegjs) for WordPress posts which is a block serializationspecificationwhich is used to generate the actualparserwhich is also bundled in this package.即文法是“规范”生成的解析器是“实现”两者同装在该包内。相关背景概念可参考 PEG.js 与 Parsing expression grammarPEG 文法每个规则按顺序尝试子规则取第一个成功匹配。二、安装包名发布在 npm 上安装命令见 README.mdnpm install wordpress/block-serialization-spec-parser --save从 package.json 可以看到版本与环境要求当前版本5.55.0运行时要求node 18.12.0npm 8.19.2主入口parser.js即构建生成的 JS 解析器依赖pegjs ^0.10.0生成 JS 解析器与phpegjs ^1.0.0-beta7生成 PHP 解析器开发依赖vitest ^5.0.0测试框架sideEffects: false可被 tree-shaking 安全处理额外导出./shared-testsJS/PHP 共享测试用例、./package.json三、基础用法README 给出的最小示例import { parse } from wordpress/block-serialization-spec-parser; parse( !-- wp:core/more --!--more--!-- /wp:core/more -- ); // [{attrs: null, blockName: core/more, innerBlocks: [], innerHTML: !--more--}]注意 README 示例中attrs显示为null这是文档撰写时的输出快照按当前文法与测试shared-tests.js的实现未携带属性时会回退为空对象{}详见下文数据结构说明。实际使用时以你自己环境里parse()的返回为准。更完整的调用形态测试中大量使用的输入包括parse( !-- wp:void /-- ); // → [{ blockName: core/void, attrs: {}, innerBlocks: [], innerHTML: , innerContent: [] }] parse( !-- wp:my/bus { is: fast } /-- ); // → [{ blockName: my/bus, attrs: { is: fast }, innerBlocks: [], innerHTML: , innerContent: [] }] parse( !-- wp:block --Before!-- wp:void /--!-- /wp:block -- ); // → [{ blockName: core/block, attrs: {}, innerBlocks: [/* 内层块 */], // innerHTML: Before, innerContent: [ Before, null ] }] parse( pBreak me/p!-- wp:block /-- ); // → [{ blockName: null, attrs: {}, innerHTML: pBreak me/p, ... }, // { blockName: core/block, ... }]四、解析输出的数据结构parse()始终返回一个数组数组中的每个元素是一个块对象字段如下由文法中的辅助函数与测试共同确认字段类型含义blockNamestring \| null块的完整名称如core/more、my/bus。自由 HTMLfreeform片段为nullattrsobject开块注释中 JSON 编码的属性对象无属性时为空对象{}innerBlocksarray嵌套在该块内部的子块数组innerHTMLstring该块内部的原始 HTML 字符串不含开/闭注释innerContentarray内部内容的分段序列HTML 片段原样保留子块位置以null占位innerContent的设计是让上层可以精确重建原始内容遍历该数组遇到字符串直接拼接遇到null则替换为对应位置的子块序列化结果。这正是 serialize-raw-block.ts 所做的事——它把innerContent中非null的片段与null处的innerBlocks[childIndex]交错重排再经getCommentDelimitedContent重新生成注释标记const content innerContent .map( ( item ) item ! null ? item : serializeRawBlock( innerBlocks[ childIndex ], options ) ) .join( \n ) .replace( /\n/g, \n ) .trim();该文件注释明确将wordpress/block-serialization-spec-parser列为“合法解析器返回的块节点格式”的权威参考之一可见innerContent结构是整个块序列化体系的事实契约。五、文法深度解析grammar.pegjs核心文件是 grammar.pegjs。文件顶部注释说明这是 Gutenberg 文档的官方规范文法以顶层规则Block_List为入口文法中嵌入了尽量少的“代码”辅助函数 每条规则的动作解析器生成器据此产出两套解析器——浏览器端 JavaScript 版与 WordPress 的 PHP 版。文法文件同时支持 JS 与 PHP 输出动作代码中用/** ?php ... ? **/注释块标注 PHP 版本实现其外是 JS 版本实现phpegjs 在生成 PHP 解析器时会取出 PHP 分支。5.1 顶层规则Block_ListBlock_List pre:$(!Block .)* bs:(b:Block html:$((!Block .)*) { return [ b, html ] })* post:$(.*) { return joinBlocks( pre, bs, post ); }语义内容被切分为「块前自由 HTMLpre→ 一系列「块 块后 HTML 片段」→ 尾部自由 HTMLpost」最终交给joinBlocks组装。joinBlocks会把每段非空 HTML 包装成blockName: null的 freeform 块夹在真正的块之间——这正是“HTML 汤HTML soup也能被解析成自由块”的机制。5.2 块的两类形态Block_Void与Block_BalancedBlock规则按顺序尝试两种子规则自闭合void块Block_Void !-- __ wp: blockName:Block_Name __ attrs:(a:Block_Attributes __ {...})? /--即!-- wp:名称 [属性] /--返回innerHTML: 、innerContent: []、attrs: attrs || {}。成对balanced块Block_Balanced s:Block_Start children:(Block / $((!Block !Block_End .)))* e:Block_End即!-- wp:名称 [属性] --与!-- /wp:名称 --之间的任意内容children递归匹配内层Block或普通文本最终由processInnerContent把 children 拆成[ innerHTML, innerBlocks, innerContent ]三元组。5.3 开块/闭块标记Block_Start与Block_EndBlock_Start !-- __ wp: blockName:Block_Name __ attrs:(...)? -- Block_End !-- __ /wp: blockName:Block_Name __ --开标记可携带可选的 JSON 属性段闭标记只校验名称。注意这里Block_End并不强制与Block_Start的blockName一致——文法层面的容错由上层逻辑负责文法只保证“能解析”。5.4 块命名规则Block_NameBlock_Name Namespaced_Block_Name / Core_Block_Name Namespaced_Block_Name $( Block_Name_Part / Block_Name_Part ) Core_Block_Name type:$( Block_Name_Part ) { return core/ type; } Block_Name_Part $( [a-z][a-z0-9_-]* )带命名空间的名称a/b原样保留如my/more→my/more无命名空间的裸名称自动补全为core/前缀如more→core/more名称片段必须以小写字母开头随后可包含小写字母、数字、_、-。这正是测试blockName is namespaced string (except freeform)所验证的行为见 shared-tests.js。5.5 JSON 属性Block_AttributesBlock_Attributes attrs:$({ (!(} __ /? --) .)* }) { return maybeJSON( attrs ); }属性是开块注释内的一对花括号包裹的 JSON。规则用否定前瞻排除提前遇到闭标记的情形再交给maybeJSON做JSON.parse解析失败时返回nullJS 端maybeJSON的 catch 分支。测试覆盖了带空格、换行、嵌套对象、长字符串等变体例如parse( !-- wp:void { value : true } /-- )[0].blockName core/void parse( !-- wp:void {\n\tvalue : true\n} /-- )[0].blockName core/void5.6 空白____ [ \t\r\n]用于标记名、属性等之间的空白分隔至少 1 个空格/制表符/换行。因此!-- wp:block / --自闭合标记/后有空格不符合Block_Void语法会被当作自由 HTML 解析——这也是 shared-tests.js 中 “invalid block comment syntax” 用例验证的行为。5.7 内嵌的辅助函数文法文件顶部声明了一组最小辅助函数生成解析器时会内嵌进去freeform(s)把一段 HTML 包装成blockName: null的自由块joinBlocks(pre, tokens, post)把「前部 HTML 块序列 后部 HTML」拼接成完整块数组maybeJSON(s)安全解析 JSON失败返回null仅 JS 需要PHP 用json_decode语义等价替代processInnerContent(list)把 children 混合列表拆分为[innerHTML, innerBlocks, innerContent]字符串进 HTML/内容块进子块/null占位。对应的 PHP 版本peg_empty_attrs、peg_process_inner_content、peg_join_blocks以/** ?php ... ? **/形式写在函数体内供 PHP 生成器提取。另外注意 PHP 版对“空属性”做了专门处理peg_empty_attrs()用json_decode({}, true)缓存空对象避免 PHP 空数组与空列表的序列化歧义。六、双端构建一份文法生成 JS 与 PHP 两套解析器package.json 中的构建脚本scripts: { prelint:js: npm run build:js, build: concurrently \npm run build:js\ \npm run build:php\, build:js: pegjs --format commonjs -o ./parser.js ./grammar.pegjs, build:php: node bin/create-php-parser.js }JS 解析器直接用pegjsCLI 把grammar.pegjs编译为 CommonJS 模块parser.js即包的主入口PHP 解析器走 bin/create-php-parser.js调用pegjs.generate并注入phpegjs插件产出parser.phpconst parser pegjs.generate( peg, { plugins: [ phpegjs ], phpegjs: { parserNamespace: null, parserGlobalNamePrefix: Gutenberg_PEG_, mbstringAllowed: false, }, } );生成出的 PHP 解析器类名为Gutenberg_PEG_Parser从 test/test-parser.php 可以看到其用法require_once __DIR__ . /../parser.php; $parser new Gutenberg_PEG_Parser(); echo json_encode( $parser-parse( file_get_contents( php://stdin ) ) );即PHP 解析器从标准输入读取文档parse()后json_encode输出——与 JS 版保持相同的数据结构契约这也是 WordPress 服务端能还原同一篇块文档的根本保证。七、测试体系一套用例同时跑 JS 与 PHPshared-tests.js 定义了jsTester与phpTester两个导出jsTester直接调用传入的parse函数覆盖以下分组输出结构始终返回数组blockName、attrs、innerBlocks、innerHTML的类型与取值通用行为多个可复用块{ref:313}、自闭合与空成对块等价、块前后 HTML soup 的捕获innerContent 占位符字符串原样保留、子块位置为null、前后片段与相邻子块的组合攻击向量10 万字符的 JSON 属性段不抛异常、!-- wp:block / --这类带多余空格的“伪 void”被当作自由文本blockName null。phpTester在检测到系统装有phpNODE_ENV test时通过spawnSync(php, [-r, echo 1;])探测时把同一套jsTester用例喂给 PHP 解析器通过php -f test-parser.php传 stdin 输入、读 stdout 结果因为 PHPjson_encode会把空关联数组序列化成[]测试做了一次attrs:[] → attrs:{}的正则替换以对齐 JS 输出测试框架层面的归一化并非解析器差异。入口 test/index.js 使用 Vitest同时注册 JS 与 PHP 两套描述块做到“同一份断言、双端验证”。八、在 Gutenberg 生态中的位置该包是块序列化体系的“规范实现”层。与之对照wordpress/block-serialization-default-parser提供默认解析器非 PEG 生成wordpress/block-serialization-spec-parser提供文法驱动、可双端生成的解析器上层wordpress/blocks的 serialize-raw-block.ts 负责把解析器产出的原始块节点重新序列化为注释标记其注释明确引用本包与 default-parser 作为“合法解析器输出格式”的权威说明。因此整条链路是grammar.pegjs规范→parser.js/parser.php解析→ 块节点对象blockName/attrs/innerBlocks/innerHTML/innerContent→serializeRawBlock再序列化。理解这份文法就等于理解了 WordPress 块标记的底层语法约束命名必须小写开头、属性必须 JSON、自闭合与成对标记的边界、以及任意 HTML 都会被兜底为自由块。九、参与贡献该包是 Gutenberg monorepo 的一部分packages/下的自包含包独立发布到 npm被 WordPress 核心及其他项目使用。若想贡献可参考项目主 CONTRIBUTING.md 以及包内的 CHANGELOG.md本地开发时注意先执行构建npm run build以生成parser.js与parser.php再通过 Vitest 运行双端测试。十、要点小结文法即规范grammar.pegjs是 WordPress 块序列化的官方 PEG 规范从顶层Block_List开始逐规则定义合法标记两类块形态自闭合!-- wp:name attrs /--与成对!-- wp:name attrs --…!-- /wp:name --命名归一裸名自动加core/前缀命名空间名原样保留片段须以小写字母开头输出契约每个块对象含blockName / attrs / innerBlocks / innerHTML / innerContent五个字段自由 HTML 的blockName为null子块位置在innerContent中以null占位双端同构pegjs phpegjs 从同一份文法生成 JS 与 PHP 两套解析器共享同一套测试用例确保浏览器端与 WordPress 服务端解析结果一致。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价