Joplin 深度解析:Turndown 定制版 HTML 转 Markdown 引擎的安装、配置与规则扩展指南

发布时间:2026/9/13 17:05:03
Joplin 深度解析:Turndown 定制版 HTML 转 Markdown 引擎的安装、配置与规则扩展指南 Joplin 深度解析Turndown 定制版 HTML 转 Markdown 引擎的安装、配置与规则扩展指南【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin导读本文围绕 Joplin 仓库中独立维护的 joplin/turndown 包展开它基于开源项目 Turndown原名 to-markdown深度定制负责把网页抓取、剪藏与富文本编辑产生的 HTML 无损地转换回 Joplin 的 Markdown 笔记格式。读完本文你将掌握该包的安装方式、全部配置选项、addRule/keep/remove/use等核心方法、规则过滤器与替换函数的编写原理并理解 Joplin 是如何在 HtmlToMd.ts 中组合这些能力完成真实转换的。一、Turndown 是什么Joplin 为什么需要一份定制版Turndown 是一个用 JavaScript 编写的 HTML 转 Markdown 库核心能力一句话即可概括Convert HTML into Markdown with JavaScript。它被广泛应用在网页正文抽取 → 笔记化的场景中。Joplin 的同步与笔记核心虽然是 Markdown但在 Web 剪藏器、Rich Text富文本编辑器、导入 Evernote/OneNote 等路径上数据往往以 HTML 形态存在因此需要一个稳定、可控、且与 Joplin 自身渲染规则如 MathJax、任务列表、资源占位符兼容的 HTML→Markdown 转换器。官方并没有直接引用上游版本而是在packages/turndown目录下维护了一个基于上游 commit97e4535的 fork包名发布为joplin/turndown见 package.json并针对性修复了大量真实使用中遇到的问题。1.1 相对上游的定制修改清单README 的 Modifications 一节完整列出了这个 fork 相对上游的改动是理解整份源码差异的索引移除链接中的 JavaScript 代码防止hrefjavascript:...被原样带入 Markdown对应 commonmark-rules.js 中filterLinkHref对javascript:前缀的过滤。阻止链接文本内出现换行链接文字中的换行会被转换为br见filterLinkContent。修复有序列表超过 9 项时的缩进对齐listItem规则中根据序号位数动态计算前缀对齐空格 .repeat(3 - indexStr.length)。支持picture标签新增rules.picture优先取子img否则回退到第一个source的srcset。修复锚点 URL 的编码空格、换行、制表符、括号分别被编码为%20、%0A、%09、%28/%29。支持命名锚点a href#internal-link配合a idinternal-link的页面内跳转可被保留。识别更多代码块特例见下方 1.2 节。处理 MathJax 块跳过渲染后的MathJaxspan转而从typemath/tex的script中提取原始公式包裹为$...$或$$...$$。允许规则自行决定内容是否转义通过规则对象的escapeContent属性实现。支持非 OL 的有序列表样式list-style-type: decimal的元素也按有序列表处理。新增preserveImageTagsWithSize选项带width/height属性的img保留为原始 HTML 而不转成 Markdown。非断行空格替换段落开头的 Unicode 非断行空格\u00A0替换为nbsp;防止渲染时被吞掉。1.2 代码块识别从猜到判断README 中Detect more types of code blocks based on special cases的实现集中在 utilities.js共有三条判定路径常规判定precode结构isCodeBlock。特例一isCodeBlockSpecialCase1td classcodepre ...——GitHub 等站点渲染代码时常用的表格包装若不识别会被当普通文本处理。特例二isCodeBlockSpecialCase2pre内联样式声明了font-family: monospace则视为代码块。判断过程使用adobe/css-tools解析内联样式后检测monospace关键字。代码块的空白处理也做了大量加固replacementForNode与join中专门针对node.isCode保留换行、避免把代码块内行首缩进误判为 Markdown 代码块标记相关修复与回归测试用例可在packages/app-cli/tests/html_to_md/code_multiline_*.html中查看。二、安装与构建2.1 npm 安装npm install joplin/turndown这是 Joplin 发布到 npm 的包名在仓库内部packages/lib通过require(joplin/turndown)引用它见 HtmlToMd.ts。2.2 浏览器直接引入script srchttps://unpkg.com/turndown/dist/turndown.js/script2.3 UMD 版本与手动构建包发布时会在lib/下生成多套产物Node 与浏览器各一份 UMD/CJS/ESMlib/turndown.umd.js与lib/turndown.browser.umd.js供 RequireJS 等场景使用。若需本地重建可克隆仓库后在packages/turndown目录执行npm run build。从 package.json 的 scripts 可见完整构建链路由 Rollup 驱动build-cjs/build-es/build-umd/build-iife分别产出 CommonJS、ES Module、UMD、IIFE 四类格式build-test用 browserify 把 test/turndown-test.js 打包为浏览器测试脚本prepare发布前自动执行构建。2.4 获得 TypeScript 类型该包自身不携带类型声明需要安装社区类型并做模块映射npm install types/turndown创建declarations.d.tsdeclare module joplin/turndown { export { default } from turndown; }然后在tsconfig.json的files数组中登记{ files: [declarations.d.ts] }三、基本用法3.1 字符串转换Node.jsvar TurndownService require(turndown) var turndownService new TurndownService() var markdown turndownService.turndown(h1Hello world!/h1)3.2 直接转换 DOM 节点turndown()的输入既可以是 HTML 字符串也可以是 DOM 节点元素节点、文档节点或文档片段节点var markdown turndownService.turndown(document.getElementById(content))底层对字符串输入的处理值得注意RootNode会把输入包裹在x-turndown idturndown-root自定义元素中再交给解析器以保证所有元素可靠地聚合到单一根节点Node 环境使用mixmark-io/domino模拟 DOM浏览器环境则优先使用原生DOMParser见 root-node.js 与 html-parser.js。转换开始前还会执行collapseWhitespace空白折叠将[ \r\n\t]压缩为单个空格避免多余空白破坏 Markdown 结构见 collapse-whitespace.js。turndown()内部依次执行process逐节点匹配规则生成片段与postProcess追加引用链接、修剪首尾空白、按collapseMultipleBlankLines折叠连续空行详见 turndown.js。四、配置选项Options选项在构造时传入var turndownService new TurndownService(options)4.1 基础选项Option合法值默认值headingStylesetext或atxsetexthr任意 Thematic break* * *bulletListMarker-、或**codeBlockStyleindented或fencedindentedfence或~~~emDelimiter_或*_strongDelimiter**或__**linkStyleinlined或referencedinlinedlinkReferenceStylefull、collapsed或shortcutfull4.2 高级选项Option合法值默认值blankReplacement规则替换函数见下文特殊规则keepReplacement规则替换函数见下文特殊规则defaultReplacement规则替换函数见下文特殊规则4.3 Joplin fork 新增的选项从 turndown.js 的默认配置中可以看到 README 之外的扩展选项Option默认值说明anchorNames[]已声明为命名锚点的 id/name 列表命中的链接生成a id.../abr 软换行时br/的替换文本两个空格是 Markdown 软换行语法disableEscapeContentfalse置为true时跳过内容转义process中直接使用原始文本preformattedCodefalse为true时PRE/CODE内的空白不再折叠传给collapseWhitespace的isPre判定preserveNestedTablesfalse嵌套表格保留为 HTMLpreserveColorStylesfalse保留span stylecolor:...前景色样式配合rules.foregroundColortightListsfalse列表项内的单一段落不额外插入空行collapseMultipleBlankLinesfalse输出阶段把连续 3 行以上空行压缩为一个空行postProcess中的/(\n\s*){3,}/gallowResourcePlaceholders未默认开启允许识别 Joplin 的not-loaded-resource图片占位符并还原为:/resourceId引用preserveImageTagsWithSizefalse带width/height的img原样保留 HTMLREADME 修改清单最后一条headingStyle与codeBlockStyle在 commonmark-rules.js 中直接生效setext对 h1/h2 用/-下划线式标题atx则用#前缀fenced模式输出围栏代码块时会自动检测代码内出现的围栏字符并递增围栏长度以避免冲突fenceSize逻辑见 commonmark-rules.js。五、核心方法5.1addRule(key, rule)注册自定义规则key是便于引用的唯一名称turndownService.addRule(strikethrough, { filter: [del, s, strike], replacement: function (content) { return ~ content ~ } })addRule返回TurndownService实例以支持链式调用。实现上它把规则unshift到规则数组头部从而获得高于 CommonMark 内置规则的优先级见 rules.js。5.2keep(filter)指定哪些元素保持为 HTML 原样输出。默认不保留任何元素。filter的取值规则与规则过滤器一致见下文第六节turndownService.keep([del, ins]) turndownService.turndown(pHello delworld/delinsWorld/ins/p) // Hello delworld/delinsWorld/ins要点keep可多次调用后添加的 keep 过滤器优先于旧的但 keep 规则优先级低于 CommonMark 内置规则与addRule添加的规则。默认的keepReplacement会输出节点outerHTML并在块级元素前后补空行还会把连续空行改写为!-- --注释防止 Markdown 将其识别为 HTML 块结束见 turndown.js。5.3remove(filter)指定哪些元素连同内容整体删除替换为空字符串。默认不删除任何元素turndownService.remove(del) turndownService.turndown(pHello delworld/delinsWorld/ins/p) // Hello Worldremove同样可多次调用后添加者优先其优先级低于 keep 规则与内置规则。Joplin 在 HtmlToMd.ts 中正是用turndown.remove(script)与turndown.remove(style)剔除网页脚本与样式。5.4use(plugin|array)应用一个或一组插件var turndownPluginGfm require(turndown-plugin-gfm) var gfm turndownPluginGfm.gfm var tables turndownPluginGfm.tables var strikethrough turndownPluginGfm.strikethrough turndownService.use(gfm) turndownService.use([tables, strikethrough])插件本质是接收TurndownService实例的函数传入数组时逐个递归调用。非函数或非数组的入参会抛出TypeError见 turndown.js。注意 Joplin 使用的是配套 fork 包joplin/turndown-plugin-gfm。5.5 其他公开方法escape(string)对字符串中的 Markdown 语法字符做转义。转义表在 turndown.js覆盖反斜杠、星号、行首-///#/、反引号、方括号、~~、数字编号点、$数学公式以及下划线使用 Unicode 属性正则\p{Punctuation}判定并提供兼容性回退正则。isCodeBlock(node)暴露代码块判定能力供调用方复用同一套isCodeBlock逻辑。六、规则扩展filter 与 replacementTurndown 的扩展核心是规则rule——一个包含filter与replacement两个属性的普通对象。段落规则即为最简示例{ filter: p, replacement: function (content) { return \n\n content \n\n } }filter负责这个节点归不归我管replacement负责把它变成什么 Markdown。6.1filter的三种形态字符串匹配同名的标签名如filter: p选择p数组匹配任一标签名如filter: [em, i]同时选择em与i函数接收(node, options)返回布尔值。例如仅当linkStyle inlined时选择带href的afilter: function (node, options) { return ( options.linkStyle inlined node.nodeName A node.getAttribute(href) ) }底层匹配逻辑见 rules.js字符串比较node.nodeName.toLowerCase()数组做包含判断函数则直接调用其他类型一律抛TypeError。6.2replacement函数的签名rules.emphasis { filter: [em, i], replacement: function (content, node, options) { return options.emDelimiter content options.emDelimiter } }参数依次为该节点的已转换内容、节点本身、TurndownService 的 options。此外 Joplin fork 还在调用链中额外传入了previousNode前一个兄弟节点用于br/连排等场景的判断见 turndown.js 与lineBreak规则。规则对象还可以声明escapeContent(node)函数返回false表示子内容不转义——MathJax 脚本、joplin-source块正是依赖这一能力保留原始反斜杠见 commonmark-rules.js。6.3 特殊规则规则作用定制入口Blank 规则处理空白节点仅含空白且非a/td/th/void 元素。覆盖所有其他规则含addRule添加的blankReplacementKeep 规则保持为 HTML 的元素块级元素与周围内容以空行分隔keepReplacementRemove 规则整体删除的元素无返回空串Default 规则兜底处理所有未被任何规则识别的节点默认输出文本内容块级元素前后补空行defaultReplacement6.4 规则优先级Turndown 顺序遍历规则集合并采用首个匹配即命中策略。完整优先级为Blank 规则若节点为空白addRule添加的规则CommonMark 内置规则Keep 规则Remove 规则Default 规则对应实现见 rules.js 的forNode。node.isBlank由 node.js 判定非 void、非空白时仍有意义A/TABLE/TH/SCRIPT等且文本全为空白。七、插件系统插件 API 为批量扩展提供了统一入口插件只是一个接收TurndownService实例的函数内部可以自由调用addRule、keep、remove、use。gfm插件即典型例子——一次性注入表格、删除线、任务列表等 GitHub Flavored Markdown 规则。Joplin 在 HtmlToMd.ts 中通过turndown.use(turndownPluginGfm)启用了这套规则。八、Joplin 中的真实集成HtmlToMd 封装Joplin 没有直接散落地调用 Turndown而是在 packages/lib/HtmlToMd.ts 中做了一层封装其ParseOptions与传给 Turndown 的turndownOpts一一对应HtmlToMd 选项传递给 TurndownanchorNamesanchorNames统一 trim 小写化preserveImageTagsWithSizepreserveImageTagsWithSizepreserveNestedTablespreserveNestedTablespreserveTableStyles/preserveColorStyles对应透传disableEscapeContentdisableEscapeContenttightListstightListscollapseMultipleBlankLinescollapseMultipleBlankLinesconvertEmbeddedPdfsToLinks通过自定义blankReplacementaddRule(pdf, ...)把embed/object中的 PDF 转为embedded_pdf链接同时它固定使用headingStyle: atx、codeBlockStyle: fenced、bulletListMarker: -、emDelimiter: *、strongDelimiter: **、br: 并remove(script)/remove(style)。这一层封装在 Joplin 的 Web 剪藏、HTML 导入等场景中被广泛复用例如packages/lib/commands/convertHtmlToMarkdown.ts、packages/lib/services/rest/routes/notes.ts都引用了HtmlToMd。此外commonmark-rules.js中还有一批服务于 Joplin 生态的特殊规则值得了解highlightmark→textinsert/superscript/subscript下划线、上标、下标保留为ins/sup/subHTMLresourcePlaceholder识别not-loaded-resource图片占位符并还原为:/resourceId资源语法ignoreMathDisplay跳过 Wikipedia/KaTeX 的 MathML 视觉回退内容mathjaxRendered/mathjaxScriptInline/mathjaxScriptBlock/mathMlScriptBlock完整的 MathJax 与 MathML 公式提取链路joplinHtmlInMarkdown带jop-noMdConvclass 的节点不做转换、原样保留 HTML用于 MD→HTML→MD 往返场景joplinSourceBlock识别joplin-editable中隐藏的joplin-source源码块实现插件渲染内容的无损还原。这些规则与 README 修改清单一一对应构成了 Joplin 剪藏链路区别于通用 Turndown 的完整差异化能力。九、测试与验证包内测试位于 packages/turndown/test/turndown-test.js配套 test/index.html 提供浏览器运行环境build_for_test.sh与publish.sh分别负责测试构建与发布。更全面的端到端回归用例散落在packages/app-cli/tests/html_to_md/目录如code_multiline_*.html、mathjax_inline、mathjax_block等它们直接验证了代码块空白、MathJax 提取等 fork 特性的真实输出。十、许可证Turndown 版权归 Dom Christie© 2017以 MIT 许可证发布见 packages/turndown/LICENSEJoplin fork 在其基础上叠加了上文所述的全部修改。【免费下载链接】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),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询