function attributeNodeToVElementMatcher(selector) {
  const key = selector.attribute
  if (!selector.operator) {
    return (element) => getAttributeValue(element, key) != null
  }
  const value = selector.value || ''

  switch (selector.operator) {
    case '=': {
      return buildVElementMatcher(value, (attr, val) => attr === val)
    }
    case '~=': {
      // words
      return buildVElementMatcher(value, (attr, val) =>
        attr.split(/\s+/gu).includes(val)
      )
    }
    case '|=': {
      // immediately followed by hyphen
      return buildVElementMatcher(
        value,
        (attr, val) => attr === val || attr.startsWith(`${val}-`)
      )
    }
    case '^=': {
      // prefixed
      return buildVElementMatcher(value, (attr, val) => attr.startsWith(val))
    }
    case '$=': {
      // suffixed
      return buildVElementMatcher(value, (attr, val) => attr.endsWith(val))
    }
    case '*=': {
      // contains
      return buildVElementMatcher(value, (attr, val) => attr.includes(val))
    }
    default: {
      throw new SelectorError(`Unsupported operator: ${selector.operator}.`)
    }
  }

  /**
   * @param {string} selectorValue
   * @param {(attrValue:string, selectorValue: string)=>boolean} test
   * @returns {VElementMatcher}
   */
  function buildVElementMatcher(selectorValue, test) {
    const val = selector.insensitive
      ? selectorValue.toLowerCase()
      : selectorValue
    return (element) => {
      const attrValue = getAttributeValue(element, key)
      if (attrValue == null) {
        return false
      }
      return test(
        selector.insensitive ? attrValue.toLowerCase() : attrValue,
        val
      )
    }
  }
}
Convert Attribute node to VElementMatcher
@param {parser.Attribute} selector
@returns {VElementMatcher}