Harper WordPress 插件:将离线隐私优先的语法检查器嵌入 WordPress 块编辑器
2026/9/14 9:18:42 网站建设 项目流程

Harper WordPress 插件:将离线隐私优先的语法检查器嵌入 WordPress 块编辑器

【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harper

Harper 是一个离线运行、隐私优先、基于 Rust 的语法检查器,而本仓库中的packages/wordpress-plugin将其能力带入了 WordPress 的块编辑器:在写作时实时检查文档内每个富文本块的语法问题,并在编辑器侧边栏中列出可忽略的问题与建议。读完本文,你将理解该插件从 PHP 入口、block.json块注册,到通过 WASM 引擎在浏览器中执行lint调用的完整链路,以及如何在本机构建并打包出可分发的harper.zip

插件定位与模块构成

根据 packages/wordpress-plugin/README.md,该目录包含 Harper 的 WordPress 插件,官方明确标注其为work-in-progress(进行中),尚属于早期原型阶段。从源码结构看,模块由三层构成:

层次文件/目录职责
PHP 入口harper.php向 WordPress 注册块类型
构建配置package.json基于wp-scripts的构建脚本与依赖声明
块前端(React)src/harper/侧边栏插件、高亮渲染、WASM 检查引擎封装

核心能力——即真正的语法检查逻辑——并不在这个插件里,而是来自同仓库的 harper.js 包(以"harper.js": "workspace:*"的方式作为 pnpm workspace 依赖引入)。插件本身负责的是“在 WordPress 编辑界面里采集文本、展示结果、管理用户偏好”这一交互层。

PHP 入口:harper.php 如何注册块

harper.php 是整个插件的 PHP 侧全部实现,逻辑非常克制:

/** * Plugin Name: Harper * Version: 0.0.1 * Requires at least: 6.7 * Requires PHP: 7.4 * License: GPL-2.0-or-later * Text Domain: harper */ declare( strict_types = 1 ); if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } function create_harper_block_init() { register_block_type( __DIR__ . '/build/harper' ); } add_action( 'init', 'create_harper_block_init' );

几个值得注意的细节:

  1. 头部元数据声明了最低环境要求:WordPress6.7、PHP7.4,许可证为 GPL-2.0-or-later——这是 WordPress 插件目录的分发前提。
  2. ABSPATH防护:直接访问该文件时立即退出,遵循 WordPress 插件的标准安全惯例。
  3. register_block_type( __DIR__ . '/build/harper' ):注册的不是 PHP 渲染逻辑,而是一个指向构建产物目录build/harper的路径。WordPress 会读取其中的 block.json 元数据,并自动处理资源入队(enqueue)。这意味着该插件是典型的元数据块(metadata-only block):所有交互逻辑都在前端 JS 中。

block.json:块的元数据契约

src/harper/block.json 是构建时build/harper目录的核心文件,完整内容为:

{ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "harper/harper", "version": "0.0.1", "title": "Harper", "category": "text", "icon": "smiley", "description": "Harper is the grammar checker that respects your privacy", "example": {}, "supports": { "html": false }, "textdomain": "harper", "editorScript": "file:./index.js", "editorStyle": "file:./index.css" }
  • apiVersion: 3使用最新的块编辑器 API;
  • supports.html: false表示该块不支持切换原始 HTML——它不产生独立的内容结构,而更像挂载在文档上的“检查器”;
  • editorScript/editorStyle仅声明编辑器资源,即该插件只作用于编辑器环境,不向文章前台注入任何脚本,这与“隐私优先、离线检查”的定位一致。

构建系统与 npm 脚本

package.json 定义了插件的构建与打包方式:

脚本命令作用
buildwp-scripts build --webpack-copy-php生产构建,并将harper.php复制进build/
startwp-scripts start --webpack-copy-php开发模式,带热重载监听
plugin-zipzip harper.zip build harper.php screenshot.png -r按 WordPress 插件格式打包可上传的 zip
packages-updatewp-scripts packages-update同步@wordpress/*依赖到仓库内 vendor 目录

依赖侧的关键信息:

  • devDependencies中的@wordpress/scripts(^30.9.0)提供wp-scripts工具链;@wp-now/wp-now(^0.1.74)用于在本地快速拉起一个 WordPress 实例做开发验证(从依赖结构看,仓库将本地 WordPress 环境纳入了开发流程)。
  • dependencies中除 React 18 与若干@wordpress/*包外,最关键的是"harper.js": "workspace:*"——检查引擎来自本 monorepo 内部,构建时会一并打包进产物。

块编辑器插件注册:index.tsx

src/harper/index.tsx 展示了插件与块编辑器(block editor)的挂载方式——它并没有注册新的块,而是注册了一个编辑器侧边栏插件(PluginSidebar)

function Sidebar() { return ( <> <PluginSidebarMoreMenuItem target="harper-sidebar" icon={Logo()}> Harper </PluginSidebarMoreMenuItem> <PluginSidebar name="harper-sidebar" title="Harper" icon={Logo}> <LinterProvider> <SidebarControl /> </LinterProvider> </PluginSidebar> </> ); } if (!window.__harperSidebarRegistered) { registerPlugin('harper-sidebar', { render: Sidebar }); window.__harperSidebarRegistered = true; }

这里通过@wordpress/edit-postPluginSidebar@wordpress/pluginsregisterPlugin,把 Harper 挂到编辑器“更多(More)”菜单下的独立侧边栏中。window.__harperSidebarRegistered是一个防重复注册的保护标志(开发模式下模块可能被重复求值)。block.json里的块名harper/harper则提供了块在文档中的占位能力,而侧边栏承载了主要的检查交互。

引擎层:WorkerLinter 与内联二进制

src/harper/LinterProvider.tsx 是整个插件与 Harper 引擎的衔接点:

import { type Linter, WorkerLinter } from 'harper.js'; import { binaryInlined } from 'harper.js/binaryInlined'; const linterContext = createContext<Linter>(new WorkerLinter({ binary: binaryInlined })); export default function LinterProvider({ children }: { children: ReactNode | ReactNode[] }) { const linter = useRef(new WorkerLinter({ binary: binaryInlined })); return <linterContext.Provider value={linter.current}>{children}</linterContext.Provider>; }

可以从源码结构中确认:

  • WorkerLinterharper.js提供的 Linter 实现,检查工作在 Web Worker 中异步进行,避免阻塞编辑器主线程;
  • binaryInlined(来自harper.js/binaryInlined子入口)提供了内联的 Rust 引擎二进制(从命名与harper-wasm子项目的存在看,即打包进 JS 的 WASM 产物),因此检查完全在浏览器本地完成,不上传任何文本——这正是 README 中 “respects your privacy” 的技术落点;
  • Provider 用useRef保证整个侧边栏生命周期内只有一个 Linter 实例,通过 React Context 向下分发;配套的useLintDescriptions()会在挂载后拉取各 lint 规则的描述文本,供侧边栏展示。

检查流水线:SidebarControl 与 useLintBoxes

交互层的核心是两个文件,它们共同回答了“WordPress 文档是怎么被实时检查的”。

文本采集与 DOM 观察

src/harper/SidebarControl.tsx 的流程:

  1. 通过DataBlock.getContainer()拿到文档内容容器(由 DataBlock.ts 基于块编辑器数据模型实现);
  2. DataBlock.getTerminalDataBlocks()取出所有“终端”数据块,并用MutationObserver监听容器子树变化,块结构变动时重新采集;
  3. 从每个块中getAllRichText()提取富文本字段,交给useLintBoxes执行检查;
  4. 检查结果通过createPortal渲染成 Highlighter 组件,把高亮框直接传送(portal)回文档 DOM,在正文对应位置显示问题标记;
  5. 侧边栏本体由SidebarTabContainer渲染,汇总展示所有lintBoxes及加载状态。

配置同步与 lint 执行

src/harper/useLintBoxes.ts 是插件与Linter接口对话最密集的地方,其updateLints回调完整展示了引擎的调用序列:

// 1. 校验“已忽略”状态,不一致则清空后重导 if ((await linter.exportIgnoredLints()) !== ignoreState) { await linter.clearIgnoredLints(); } // 2. 设置方言(如 en-US / en-GB) await linter.setDialect(dialect); // 3. 导入个人词典 if (personalDictionary) { await linter.importWords(personalDictionary); } // 4. 同步 lint 规则配置 if (JSON.stringify(await linter.getLintConfig()) !== JSON.stringify(config)) { await linter.setLintConfig(config); } // 5. 恢复被用户忽略的具体 lint 项 if (ignoreState) { await linter.importIgnoredLints(ignoreState); } // 6. 并行 lint 所有富文本字段 const newLints = await Promise.all( richTexts.map((richText) => linter.lint(richText.getTextContent())) );

这段代码揭示了引擎侧的完整能力面:setDialect(方言,对应 useDialect.ts,可推断基于@wordpress/preferences-persistence持久化)、importWords(个人词典,usePersonalDictionary.ts)、getLintConfig/setLintConfig(按规则粒度的开关配置,useLintConfig.ts)、importIgnoredLints/exportIgnoredLints/clearIgnoredLints(“忽略此问题”状态,useIgnoredLintState.ts)。每个 hook 各自管理一块偏好状态,插件采取**“比对—按需推送”**的策略:仅当本地状态与引擎内状态不一致时才写入引擎,避免无谓的重复导入。

执行层面还有两个工程细节:

  • 每个富文本元素建立独立MutationObserver(监听childList/characterData/subtree),打字即触发updateLints重新检查——实时性由此而来;
  • 高亮框的位置通过requestAnimationFrame每帧重算(源码注释坦承 “Probably overkill”,并留有 TODO 说明后续会改为更惰性的布局回调),因为块编辑器中滚动、折叠、拖拽都会改变目标元素的位置。

本地开发、构建与打包

结合 package.json 的脚本,完整工作流如下(命令在该目录下执行):

# 开发模式:构建 + 热重载 npm run start # 生产构建(产物输出到 build/) npm run build # 打包 WordPress 插件 zip(build 目录 + harper.php + 截图) npm run plugin-zip

--webpack-copy-php参数保证harper.php会被复制进build/,最终 zip 的结构即 WordPress 标准插件包形态:harper.zip内含build/(块资源)、harper.php(入口)与screenshot.png。开发时配合@wp-now/wp-now依赖,可快速在本地得到一个 WordPress 环境来加载并调试该插件。README 中提到的贡献说明详见 Harper 官方在线文档的 WordPress 贡献指南部分。

现状与演进方向

需要再次强调 README 的定调:插件是 work-in-progress,"Here be dragons!"。版本号在 PHP 头、block.jsonpackage.json三处统一为0.0.1,表明它仍处于原型迭代期。已成型的核心架构——PHP 元数据注册 + React 侧边栏 + WASM 引擎 Worker 化执行 + 偏好状态“比对推送”同步——已经比较完整;而 useLintBoxes.ts 中每帧重算高亮位置等 TODO,则指明了性能优化是当前明确的改进方向。对于想阅读 Harper 引擎实现细节的读者,可以继续深入 harper.js 的 TypeScript 封装与 harper-core 的 Rust 规则集。

【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harper

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询