setAttribute = (vTree, domNode, name, value) => {
  const isObject = typeof value === 'object' && value;
  const isFunction = typeof value === 'function';
  const isSymbol = typeof value === 'symbol';
  const isEvent = name.indexOf('on') === 0;
  const anyNode = /** @type {any} */ (domNode);

  // Events must be lowercased otherwise they will not be set correctly.
  const lowerName = isEvent ? name.toLowerCase() : name;

  // Runtime checking if the property can be set.
  const blocklistName = 's-' + vTree.nodeName + '-' + lowerName;

  /** @type {HTMLElement} */
  const htmlElement = /** @type {any} */ (domNode);

  // Since this is a property value it gets set directly on the node.
  if (allowlist.has(blocklistName)) {
    anyNode[lowerName] = value;
  }
  else if (!blocklist.has(blocklistName)) {
    try {
      anyNode[lowerName] = value;
      allowlist.add(blocklistName);
    }
    catch {
      blocklist.add(blocklistName);
    }
  }

  // If the value is not an object, function, or symbol, then attempt to
  // set as an attribute. If the value is one of the excluded types, they
  // will be set below.
  if (!isObject && !isFunction && !isSymbol) {
    // For boolean/empty attributes, do not try and set a value, just an empty
    // string.
    const emptyValue = value === null || value === undefined || value === true;
    htmlElement.setAttribute(lowerName, emptyValue ? EMPTY.STR : value);
  }
  // Support patching an object representation of the style object.
  else if (isObject && lowerName === 'style') {
    const valueKeys = /** @type {any} */ (keys(value));

    for (let i = 0; i < valueKeys.length; i++) {
      htmlElement.style[valueKeys[i]] = value[valueKeys[i]];
    }
  }
}
Sets an attribute on an element.

@param {VTree} vTree
@param {ValidNode} domNode
@param {string} name
@param {any} value

@return {void}