Turndown 源码分析:三、规则集`rules.js` REV1

2023-10-13 09:22:48 浏览数 (2)

代码语言:javascript复制
/**
 * Manages a collection of rules used to convert HTML to Markdown
 */
export default function Rules (options) {
  // 配置
  this.options = options
  // 保留规则列表
  this._keep = []
  // 移除规则列表
  this._remove = []
 
  // 空白规则
  this.blankRule = {
    replacement: options.blankReplacement
  }

  // 保留规则
  this.keepReplacement = options.keepReplacement

  // 默认规则
  this.defaultRule = {
    replacement: options.defaultReplacement
  }

  // 初始化常规规则列表
  this.array = []
  for (var key in options.rules) this.array.push(options.rules[key])
}

Rules.prototype = {
  add: function (key, rule) {
    // 在规则表前端插入指定规则,
    // 遍历的顺序是从前向后,所以新插入的规则会优先匹配。
    this.array.unshift(rule)
  },

  keep: function (filter) {
    // 在保留列表中插入指定的过滤器。
    this._keep.unshift({
      filter: filter,
      replacement: this.keepReplacement
    })
  },

  remove: function (filter) {
    // 在移除列表中插入指定的过滤器。
    this._remove.unshift({
      filter: filter,
      replacement: function () {
        return ''
      }
    })
  },

  forNode: function (node) {
    // 判断节点是否是空白,如果是的话返回空白规则
    if (node.isBlank) return this.blankRule
    var rule

    // 依次在常规、保留、移除规则列表中寻找匹配节点的规则,如果成功就返回
    if ((rule = findRule(this.array, node, this.options))) return rule
    if ((rule = findRule(this._keep, node, this.options))) return rule
    if ((rule = findRule(this._remove, node, this.options))) return rule

    // 都没找到就返回默认规则
    return this.defaultRule
  },

  // 等价于 this.array.forEach
  forEach: function (fn) {
    for (var i = 0; i < this.array.length; i  ) fn(this.array[i], i)
  }
}

function findRule (rules, node, options) {
  // 遍历规则集的每个规则
  for (var i = 0; i < rules.length; i  ) {
    var rule = rules[i]
    // 将其与节点匹配,成功则返回当前规则
    if (filterValue(rule, node, options)) return rule
  }
  // 否则返回空值
  return void 0
}

function filterValue (rule, node, options) {
  var filter = rule.filter
  if (typeof filter === 'string') {
    // 如果过滤器是字符串,判断是否和节点名称一致
    if (filter === node.nodeName.toLowerCase()) return true
  } else if (Array.isArray(filter)) {
    // 如果过滤器是数组,查看是否包含节点名称
    if (filter.indexOf(node.nodeName.toLowerCase()) > -1) return true
  } else if (typeof filter === 'function') {
    // 如果过滤器是函数,在节点上调用它来得到匹配结果
    if (filter.call(rule, node, options)) return true
  } else {
    // 否则抛出异常
    throw new TypeError('`filter` needs to be a string, array, or function')
  }
}

0 人点赞