Harper Node.js 集成实战:harper.js 中 LocalLinter 的用法、限制与示例解析
2026/9/14 11:23:58 网站建设 项目流程

Harper Node.js 集成实战:harper.js 中 LocalLinter 的用法、限制与示例解析

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

Harper 官方文档《Using Harper in Node.js》指出,harper.js可以在 Node.js 环境中运行,但有一个关键约束:Node.js 环境下无法使用WorkerLinter,只能使用LocalLinter;同时由于harper.js是纯 ECMAScript 模块,需要相对较新版本的 Node.js 来导入。本文围绕这两个约束展开,结合 monorepo 中可运行的示例工程packages/harper.js/examples/commonjs-simple/,完整演示如何在 Node.js 中初始化 Harper 检查器、执行语法检查并解析结果与资源释放流程。

为什么 Node.js 环境只能用 LocalLinter

理解 Node.js 集成的第一步,是弄清harper.js提供两种Linter实现的差异。Linter是唯一的顶层接口,其两个实现是LocalLinterWorkerLinter,接口定义见 Linter.ts。

两者的核心区别在于 WebAssembly 模块的实例化位置:

  • LocalLinter:在同一事件循环(same event loop)中异步实例化并准备 Harper 的 WebAssembly 模块。文档明确指出,这会带来较高的 LCP(Largest Contentful Paint)开销,因此只推荐用于"事件循环不会同时处理其他对延迟敏感的工作"的场景,原文结论是:LocalLinters are not for the web(LocalLinter不适合网页环境)。
  • WorkerLinter:在 Web Worker 内实例化 WebAssembly 模块,不阻塞事件循环,适合交互式 Web 应用。

而 Web Worker 属于浏览器侧的 API。Node.js 运行时中,harper.jsWorkerLinter所依赖的 web 特定 API(如 Worker 全局构造器及相关消息通道)并不以相同形态存在,因此官方文档《Linting With harper.js》(linting/+page.md)在说明WorkerLinter面向浏览器交互场景的同时,把 Node.js 场景导向了LocalLinter。示例代码中的注释也直接点明了这一点:

// We cannot use `WorkerLinter` on Node.js since it relies on web-specific APIs.

这并非 Node.js 的能力缺陷。恰恰相反,对于 CLI 工具、构建脚本、CI 任务、数据处理管道这类无界面阻塞顾虑的场景,LocalLinter在事件循环内直接运行 WASM 反而是最直接、无 IPC 开销的方案。LocalLinter的高延迟代价主要影响的是浏览器的 LCP 指标,而 Node.js 脚本的"延迟敏感工作"通常由调用方自己安排(例如在批量处理开始时统一调用setup()预热)。

从源码结构看,Linter接口为所有实现提供了setup()方法(见 Linter.ts):

Complete any setup that is necessary before linting. This may include downloading and compiling the WebAssembly binary. This setup will complete when needed regardless of whether you call this function. This function exists to allow you to do this work when it is of least impact to the user experiences.

也就是说,即便不显式调用setup(),首次lint()时也会自动完成 WASM 的下载与编译;但如果你希望把这段耗时工作挪到脚本生命周期的低峰时段(例如读取输入文件时),可以手动预热。

ESM 要求:harper.js 是 ECMAScript 模块

Node.js 集成需要第二个前提:harper.js是一个 ECMAScript 模块,必须在相对较新、支持 ESM 的版本(Node.js 12 及以上,14/16 为稳定形态)中导入。

这一点可以从包清单 harper.js/package.json 得到确认:

  • 顶层声明"type": "module",即整个包以 ESM 语义发布;
  • exports字段只暴露import条件(没有require/default回退),例如:
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }, "./binary": { "types": "./dist/binary.d.ts", "import": "./dist/binary.js" }, "./slimBinary": { "types": "./dist/slimBinary.d.ts", "import": "./dist/slimBinary.js" }, "./binaryInlined": { "types": "./dist/binaryInlined.d.ts", "import": "./dist/binaryInlined.js" }, "./slimBinaryInlined": { "types": "./dist/slimBinaryInlined.d.ts", "import": "./dist/slimBinaryInlined.js" } }

两个对 Node.js 使用方有实际意义的细节:

  1. 子路径导出harper.js/binary:示例代码用import('harper.js/binary')获取binary(WASM 二进制模块),这正是LinterInit.binary字段需要的值(见 Linter.ts 中LinterInit的定义)。
  2. binarybinaryInlined的取舍binaryInlined变体把 WASM 内联进 JS 产物,适合不想处理独立二进制文件分发的问题的场景;binary变体则按需加载。Node.js 本地运行两者皆可,具体选择取决于打包方式。

官方示例工程:commonjs-simple

文档给出的完整示例位于 monorepo 的 examples/commonjs-simple/index.js。该目录附带自己的 package.json 与 README:

  • 依赖声明为"harper.js": "workspace:*"(monorepo 工作区引用);
  • 启动命令为node index.js
  • README 说明可用 pnpm 运行:
pnpm install pnpm start

下面给出完整示例代码(与仓库文件一致),并逐段解读:

async function main() { const harper = await import('harper.js'); const { binary } = await import('harper.js/binary'); // We cannot use `WorkerLinter` on Node.js since it relies on web-specific APIs. // This constructs the linter to consume American English. const linter = new harper.LocalLinter({ binary, dialect: harper.Dialect.American, }); try { const lints = await linter.lint('This is a example of how to use `harper.js`.'); console.log('Here are the results of linting the above text:'); for (const lint of lints) { console.log(' - ', lint.span().start, ':', lint.span().end, lint.message()); if (lint.suggestion_count() !== 0) { console.log('Suggestions:'); for (const sug of lint.suggestions()) { console.log( '\t - ', sug.kind() === harper.SuggestionKind.Remove ? 'Remove' : 'Replace with', sug.get_replacement_text(), ); } } } } finally { await linter.dispose(); } } main();

初始化:LocalLinter 与方言选择

const linter = new harper.LocalLinter({ binary, dialect: harper.Dialect.American, });

构造参数对应LinterInit接口(Linter.ts):

字段必需说明
binaryWASM 二进制模块或路径,示例中使用harper.js/binary导出的binary
dialectHarper 使用的英语方言;省略时默认美式英语(American English),示例中显式传入harper.Dialect.American

注意方言是构造期传入的,运行期也可以通过setDialect(dialect)/getDialect()切换(见 Linter.ts)。

执行 lint 并解析结果

const lints = await linter.lint('This is a example of how to use `harper.js`.');

lint(text)返回Promise<Lint[]>。示例对每个 lint 打印三类信息:

  • lint.span().start/lint.span().end:问题所在文本片段的起止偏移,便于定位到源文本;
  • lint.message():人类可读的问题描述;
  • 修复建议:lint.suggestion_count()判断是否存在建议,lint.suggestions()遍历建议列表,再用sug.kind() === harper.SuggestionKind.Remove区分"删除"与"替换",最后通过sug.get_replacement_text()取出建议的替换文本。

这里体现了 Harper 的核心价值:不仅发现问题,还尽量自动生成修复。若要把某条建议实际应用到文本上,接口还提供了applySuggestion(text, lint, suggestion),返回修改后的完整文本(见 Linter.ts)。

示例文本'This is a example of how to useharper.js.'故意包含语法错误(冠词 "a" 应为 "an"),运行后控制台会输出形如下面的结果——每条 lint 一行,包含偏移区间与消息,若有修复建议则逐条列出:

Here are the results of linting the above text: - 9 : 10 a should probably be "an". Suggestions: - Replace with an

(具体输出取决于当前版本的规则集,此处仅示意输出结构。)

资源释放:dispose 的必要性

} finally { await linter.dispose(); }

dispose()释放该 linter 实例持有的资源(见 Linter.ts)。LocalLinter在进程内实例化了完整的 WASM 运行时,长期运行的 Node.js 进程(例如常驻服务、多租户工具进程)若反复创建 linter 而不释放,会造成内存累积。示例用try/finally保证即使lint()抛错也会释放,这是 Node.js 侧集成的推荐写法。

Linter 接口速览:Node.js 场景常用能力

虽然文档主体聚焦最小可用示例,但 Linter 接口定义的全部方法在 Node.js 中同样可用(两个实现共用同一接口)。与 CLI/批处理场景直接相关的几组能力:

  • 配置管理getLintConfig()/setLintConfig(config)/setLintConfigWithJSON(config),以及getLintDescriptions()/getLintDescriptionsHTML()获取规则的 Markdown/HTML 描述,便于在文档或工具 UI 中渲染规则说明;
  • 忽略与词表ignoreLint(source, lint)ignoreLintHash(hash)exportIgnoredLints()/importIgnoredLints(json)提供隐私友好的"忽略规则"持久化(导出的是 hash 而非原文);importWords(words)/exportWords()支持批量导入自定义词条(接口注释提示这是重量级操作,建议批量调用);
  • 文本辅助toTitleCase(text)(Chicago 风格标题大写)、isLikelyEnglish(text)/isolateEnglish(text)(接口注释标注算法属于"proof of concept"阶段,效果有限,生产使用需谨慎评估);
  • 统计summarizeStats(start?, end?)generateStatsFile()/importStatsFile(statsFile)支持导出统计日志。

小结

  • harper.js在 Node.js 中的唯一实现约束:WorkerLinter依赖 Web 侧 API 不可用,须使用LocalLinter(文档 node/+page.md 的核心论点);
  • LocalLinter在同事件循环中实例化 WASM,存在高延迟代价,但这主要约束浏览器 LCP,Node.js 的 CLI/批处理场景反而是其最佳归宿,可用setup()在低峰期预热;
  • 包为纯 ESM("type": "module"exportsimport条件),需要相对较新的 Node.js 版本,示例中用动态import()加载harper.jsharper.js/binary两个入口;
  • 可运行参考:examples/commonjs-simple(pnpm install && pnpm start),覆盖初始化、dialect配置、lint 结果解析、建议分类与dispose()资源释放的完整闭环。

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

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

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

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

立即咨询