///
import xpath from '@getlang/xpath'
import { selectAll, selectOne } from 'css-select'
import { parse as parseCss } from 'css-what'
import type { AnyHtmlNode } from 'domhandler'
import { textContent } from 'domutils'
import { parse as parse5 } from 'parse5'
import { adapter } from 'parse5-htmlparser2-tree-adapter'
import {
invariant,
NullSelection,
NullSelectionError,
SelectorSyntaxError,
} from '../core/errors.js'
import './html/patch-dom.js'
export { Element } from 'domhandler'
export const parse = (html: string): AnyHtmlNode => {
return parse5(html, { treeAdapter: adapter })
}
const selectXpath = (el: AnyHtmlNode, selector: string, expand: boolean) => {
try {
const parseXpath = new xpath.XPathParser()
parseXpath.parse(selector)
} catch (e) {
throw new SelectorSyntaxError('XPath', selector, { cause: e })
}
let root = el
if (el.nodeType === 9) {
// Document -> HtmlElement
const html = el.childNodes.find(x => x.nodeType === 1 && x.name === 'html')
invariant(html, new NullSelectionError(selector))
root = html
}
const value = xpath.select(selector, root)
if (expand) {
return value
}
return value.length ? value[0] : new NullSelection(selector)
}
const selectCss = (el: AnyHtmlNode, selector: string, expand: boolean) => {
try {
parseCss(selector)
} catch (e) {
throw new SelectorSyntaxError('CSS', selector, { cause: e })
}
const value = expand ? selectAll(selector, el) : selectOne(selector, el)
if (expand) {
return value ?? []
}
return value === null ? new NullSelection(selector) : value
}
export const select = (el: AnyHtmlNode, selector: string, expand: boolean) => {
return /^(\/|\.\/)/.test(selector)
? selectXpath(el, selector, expand)
: selectCss(el, selector, expand)
}
export const toValue = (el: AnyHtmlNode) => {
let str = ''
if (el.nodeType === 2) {
str = el.value
} else if (textContent(el)) {
str = textContent(el).replaceAll(/\s+/g, ' ')
}
return str.trim()
}
export const findLink = (el: AnyHtmlNode) => {
const tag = el.type === 'tag' && el.name
switch (tag) {
case 'a':
return selectXpath(el, '@href', false)
case 'img':
return selectXpath(el, '@src', false)
default:
return el
}
}