
Joplin Web Clipper 中 Readability 库的维护指南内容脚本文件清单、版本标注规范与集成原理【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplinJoplin Web Clipper 依靠 Mozilla 开源的 Readability 库在浏览器内容脚本content script中实现“简化页面”阅读模式剪藏与“页面是否可读”检测。由于内容脚本的加载机制限制该仓库采用手动复制整份库文件的维护方式并强制要求在每个文件顶部标注上游 commit 与版本号。本文以 packages/app-clipper/content_scripts/README.md 为骨架结合index.js、service_worker.mjs与manifest.json源码完整说明需要维护的三个文件、各自的职责、更新流程及在剪藏管线中的实际调用关系帮助维护者安全、可追溯地升级这套随仓库分发的 Readability 代码。为什么 Readability 必须以“手动复制”方式维护原文档开宗明义“Because of the way content scripts are loaded, we need to manually copy the whole Readability files here. That should be fine since they rarely change.”在 Joplin Web Clipper 的实现中内容脚本的入口 packages/app-clipper/content_scripts/index.js 是一个立即执行的 IIFE其中直接以全局函数的方式调用 Readability 相关 API例如const readability new Readability(documentForReadability()); const article readability.parse();以及const ok isProbablyReaderable(documentForReadability());也就是说Readability、isProbablyReaderable这两个符号在index.js中被当作全局作用域中的函数直接使用而不是通过import/require引入的模块。配合 packages/app-clipper/content_scripts/setUpEnvironment.js 中“TypeScript 编译产物使用 CommonJSexports而浏览器环境没有exports因此需要window.exports ?? {}兜底”的注释可以推断内容脚本运行在浏览器页面上下文缺乏 Node 风格的模块解析能力因此最稳妥的做法是把上游 Readability 的完整源码含其依赖的 JSDOMParser直接平铺复制到仓库内让它们在脚本执行时挂载到全局作用域。这也是 README 强调“手动复制整份文件”的根本原因。需要维护的三个文件及其职责原文档明确列出更新时需要同步的三个文件文件用途上游版本见文件首行Readability.js核心文章提取器提供Readability(doc, options)构造器与parse()方法v0.4.4commit49d345aReadability-readerable.js提供isProbablyReaderable(doc)函数用于预估页面是否值得走 Readability 解析v0.4.4commit49d345aJSDOMParser.js轻量级 DOMParser 实现供 Readability 在受限环境下解析 HTMLv0.4.1commit28843b6Readability.js核心文章提取器Readability.js是 Readability 库的主文件源码共 2300 余行基于 Arc90 的 readability.js 1.7.1 演进而来。它在仓库内的调用点是 index.js 的readabilityProcess()function readabilityProcess() { if (isPagePdf()) throw new Error(Could not parse PDF document with Readability); const readability new Readability(documentForReadability()); const article readability.parse(); if (!article) throw new Error(Could not parse HTML document with Readability); return { title: article.title, body: article.content, }; }注意其中的关键细节documentForReadability()会先对当前页面执行document.cloneNode(true)再交给 Readability 处理。这是因为Readability 会直接修改传入的 document克隆是为了保护原始网页不被破坏function documentForReadability() { // Readability directly change the passed document so clone it so as // to preserve the original web page. return document.cloneNode(true); }Readability-readerable.js可读性预检Readability-readerable.js只导出一个函数isProbablyReaderable(doc, options)返回布尔值用于预测Readability.parse()是否可能成功。它通过一组正则unlikelyCandidates、okMaybeItsACandidate等对页面元素做启发式打分。在剪藏管线中它对应isProbablyReaderable命令} else if (command.name isProbablyReaderable) { const ok isProbablyReaderable(documentForReadability()); return { name: isProbablyReaderable, value: ok }; }值得注意的维护细节该文件头部注释特别提醒——其中的两条正则表达式与Readability.js中重复定义两处必须保持同步These two regular expressions are duplicated in Readability.js. Please keep both copies in sync.。因此升级时若上游修改了评分正则需要同时检查两份拷贝。JSDOMParser.js轻量 DOM 解析器JSDOMParser.js是 Readability 的依赖项提供“可以在 Web Worker 中安全使用的相对轻量的 DOMParser”。从源码注释可以确认它的能力边界只支持格式良好的 HTML/XML直接解析 XHR 拿到的字符串可能出错官方建议在主线程用XMLSerializer.serializeToString()序列化后再传入不支持 Live NodeListgetElementsByTagName()等方法返回普通数组节点增删后需要手动维护列表。在Readability.js内部通过this._doc.firstChild.__JSDOMParser__检测文档是否由 JSDOMParser 创建并据此切换某些 DOM 行为的实现路径例如是否处理_isLiveNodeList详见 Readability.js 中的相关分支。更新流程版本与 commit 标注规范原文档对更新动作给出了两条硬性要求完整替换三个文件整份复制不做裁剪在每个文件顶部添加 commit 与版本号例如// v0.4.4 - https://github.com/mozilla/readability/commit/49d345a455da1f4aa93f8b41e0f50422f9959c7c仓库中三个文件的首行注释正是这一规范的落地证据Readability.js与Readability-readerable.js标注 v0.4.4commit49d345aJSDOMParser.js标注 v0.4.1commit28843b6。这条标注不是装饰它承担三个作用可追溯出现 bug 时能快速定位对应上游版本与源码可审计Review 时一眼看出当前仓库与上游的版本差距判断是否值得升级可对照需要排查问题时可直接对照该 commit 的上游代码差异。由于 README 明确说明“这些文件很少变化”这种手动脉冲式升级而非每次构建都从上游拉取是刻意选择的低维护成本策略只在必要时如上游修复了重要解析 bug才升级且每次升级都留下版本痕迹。在剪藏管线中的完整调用链结合 packages/app-clipper/manifest.json 与 service_worker.mjs 可以还原 Readability 相关命令的完整链路用户在浏览器中触发快捷键命令如clipSimplifiedPageservice_worker.mjs的sendClipMessage()将其映射为simplifiedPageHtml消息case clipSimplifiedPage: message.name simplifiedPageHtml; break;随后通过browser_.tabs.sendMessage(tabId, message)发送给内容脚本。index.js 的browser_.runtime.onMessage监听器收到命令进入prepareCommandResponse()simplifiedPageHtml调用readabilityProcess()提取article.title与article.content再配合getImageSizes()、getAnchorNames()组装成clippedContent/sendContentToJoplin响应异常降级若 Readability 解析失败PDF 页面、无文章内容等捕获异常后回退到completePageHtml全页模式并附加warning Could not retrieve simplified version of page - full page has been saved instead.见 index.js。响应通过browser_.runtime.sendMessage(response)返回给扩展后台最终由桌面端剪藏服务器处理。另一个相关命令是isProbablyReaderable对应快捷命令未在manifest.json中单独暴露但由内容脚本直接支持它返回布尔值可用于剪藏界面预判当前页面是否适合“简化页面”模式。与 Readability 无关但与内容脚本强相关的同级代码升级 Readability 时建议顺带回归验证index.js中与解析管线相邻的处理逻辑因为它们共同决定剪藏质量preProcessDocument()对不可见元素display: none、visibility: hidden以及script、select、button等添加joplin-clipper-hidden类并在克隆文档中由cleanUpElement()移除cleanUpElement()处理img/svg的尺寸硬编码、input/textarea的值导出data-joplin-clipper-value、embed/object的绝对 URL 化hardcodePreStyles()与addSvgClass()为代码块与 SVG 后续 Markdown 转换做准备。这些函数位于 index.js 中虽不属于 Readability 库本身但与简化/完整页面剪藏共用同一条数据链路属于升级后的人工回归重点。维护清单与自检要点综合原文档与仓库实现一次完整的 Readability 升级建议按以下步骤执行从上游获取三个文件的新版本源码Readability.js、Readability-readerable.js、JSDOMParser.js整份替换到 packages/app-clipper/content_scripts/ 目录在每个文件顶部更新版本号与 commit 标注行若上游版本未变则保持原标注检查Readability-readerable.js与Readability.js中重复定义的正则unlikelyCandidates、okMaybeItsACandidate是否仍保持一致回归验证以下命令路径simplifiedPageHtml简化页面、isProbablyReaderable可读性预检、completePageHtml降级兜底路径重点测试 PDF 页面isPagePdf()应直接拒绝走 Readability与无法提取文章的页面应正确降级并携带 warning。按此流程操作即可在保持内容脚本加载机制不变的前提下安全地跟踪上游 Readability 的演进同时确保每一次升级都有明确的版本记录可供追溯。【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考