p1: /v2/scrape, /v1/fetch, auth, CLI, contract tests
Single Fetcher.Fetch on the Guard that holds DenyPrefixes. skipTlsVerification is ignored. No 502. keys add writes the secret only to --out. Release binary has no -test-allow-prefix.
This commit is contained in:
Generated
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func getFirstChildNode(startNode *html.Node, matchFn func(n *html.Node) bool) *html.Node {
|
||||
node := startNode.FirstChild
|
||||
for node != nil {
|
||||
name := dom.NodeName(node)
|
||||
if name == "span" {
|
||||
// A span has no special meaning. So we just skip it...
|
||||
node = dom.GetNextNeighborNode(node)
|
||||
} else if matchFn(node) {
|
||||
return node
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLastChildNode(startNode *html.Node, matchFn func(n *html.Node) bool) *html.Node {
|
||||
node := startNode.LastChild
|
||||
for node != nil {
|
||||
name := dom.NodeName(node)
|
||||
if name == "span" {
|
||||
// A span has no special meaning. So we just skip it...
|
||||
node = dom.GetPrevNeighborNode(node)
|
||||
} else if matchFn(node) {
|
||||
return node
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func AddSpace(ctx context.Context, doc *html.Node, isOuterNode, isInnerNode func(*html.Node) bool) {
|
||||
node := doc
|
||||
for node != nil {
|
||||
if isOuterNode(node) {
|
||||
firstChild := getFirstChildNode(node, isInnerNode)
|
||||
if firstChild != nil {
|
||||
prev := getPrevTextNode(node)
|
||||
if prev != nil {
|
||||
prev.Data = prev.Data + " "
|
||||
}
|
||||
}
|
||||
|
||||
lastChild := getLastChildNode(node, isInnerNode)
|
||||
if lastChild != nil {
|
||||
next := getNextTextNode(node)
|
||||
if next != nil {
|
||||
next.Data = " " + next.Data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = dom.GetNextNeighborElement(node)
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func collectAdjacentNodes(node *html.Node, matchFn func(n *html.Node) bool) []*html.Node {
|
||||
var collectedNodes []*html.Node
|
||||
|
||||
node = node.NextSibling
|
||||
for node != nil {
|
||||
name := dom.NodeName(node)
|
||||
if name == "span" {
|
||||
// A span has no special meaning. So we just skip it...
|
||||
node = dom.GetNextNeighborNode(node)
|
||||
} else if matchFn(node) {
|
||||
collectedNodes = append(collectedNodes, node)
|
||||
node = dom.GetNextNeighborNodeExcludingOwnChild(node)
|
||||
} else {
|
||||
// Return the collected nodes
|
||||
return collectedNodes
|
||||
}
|
||||
}
|
||||
|
||||
return collectedNodes
|
||||
}
|
||||
|
||||
func mergeChildren(destinationNode *html.Node, nodes ...*html.Node) {
|
||||
for _, node := range nodes {
|
||||
// We move all the children to the `destinationNode`.
|
||||
children := dom.AllChildNodes(node)
|
||||
for _, child := range children {
|
||||
dom.RemoveNode(child)
|
||||
destinationNode.AppendChild(child)
|
||||
}
|
||||
dom.RemoveNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
func MergeAdjacent(doc *html.Node, matchFn func(*html.Node) bool) {
|
||||
node := doc
|
||||
|
||||
for node != nil {
|
||||
if matchFn(node) {
|
||||
nextNodes := collectAdjacentNodes(node, matchFn)
|
||||
|
||||
mergeChildren(node, nextNodes...)
|
||||
}
|
||||
|
||||
node = dom.GetNextNeighborElement(node)
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - //
|
||||
|
||||
func MergeAdjacentTextNodes(n *html.Node) {
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var prev *html.Node
|
||||
for c := n.FirstChild; c != nil; {
|
||||
next := c.NextSibling
|
||||
if c.Type == html.TextNode && prev != nil && prev.Type == html.TextNode {
|
||||
// Combine adjacent text nodes
|
||||
prev.Data += c.Data
|
||||
n.RemoveChild(c)
|
||||
} else {
|
||||
MergeAdjacentTextNodes(c)
|
||||
prev = c
|
||||
}
|
||||
c = next
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// TODO: make this configurable via the options???
|
||||
func getMarkdownStructure(name string) string {
|
||||
switch name {
|
||||
case "#document", "html", "head", "body",
|
||||
"blockquote", "ul", "ol", "li":
|
||||
// A container block can also contain other blocks.
|
||||
return "container_block"
|
||||
|
||||
// Note: "p" would also be part of "leaf_block"
|
||||
case "hr", "pre",
|
||||
"h1", "h2", "h3", "h4", "h5", "h6":
|
||||
// Leaf blocks can contain inline content
|
||||
// but NOT other blocks.
|
||||
return "leaf_block"
|
||||
|
||||
case "#text", "span", "code",
|
||||
"b", "strong", "i", "em",
|
||||
"a", "img", "br":
|
||||
return "inline"
|
||||
|
||||
case "div", "p":
|
||||
// Since these are just placing newlines,
|
||||
// we dont categorize them.
|
||||
return ""
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func headingAlternative(ctx context.Context, node *html.Node) {
|
||||
node.Data = "strong"
|
||||
|
||||
newChild := &html.Node{
|
||||
Type: html.ElementNode,
|
||||
Data: "br",
|
||||
}
|
||||
node.Parent.InsertBefore(newChild, node.NextSibling)
|
||||
}
|
||||
func blockquoteAlternative(ctx context.Context, node *html.Node) {
|
||||
newBefore := &html.Node{Type: html.TextNode, Data: ` "`}
|
||||
node.Parent.InsertBefore(newBefore, node)
|
||||
|
||||
node.Data = "span"
|
||||
|
||||
newAfter := &html.Node{Type: html.TextNode, Data: `" `}
|
||||
node.Parent.InsertBefore(newAfter, node.NextSibling)
|
||||
}
|
||||
func preAlternative(ctx context.Context, node *html.Node) {
|
||||
node.Data = "code"
|
||||
}
|
||||
func hrAlternative(ctx context.Context, node *html.Node) {
|
||||
dom.RemoveNode(node)
|
||||
}
|
||||
|
||||
// TODO: make this configurable via the options?
|
||||
var alternatives = map[string]func(ctx context.Context, node *html.Node){
|
||||
"h1": headingAlternative,
|
||||
"h2": headingAlternative,
|
||||
"h3": headingAlternative,
|
||||
"h4": headingAlternative,
|
||||
"h5": headingAlternative,
|
||||
"h6": headingAlternative,
|
||||
"blockquote": blockquoteAlternative,
|
||||
"pre": preAlternative,
|
||||
"hr": hrAlternative,
|
||||
}
|
||||
|
||||
func LeafBlockAlternatives(ctx context.Context, doc *html.Node) {
|
||||
var finder func(node *html.Node, isInsideLeafBlock bool, isInsideInline bool)
|
||||
finder = func(node *html.Node, isInsideLeafBlock bool, isInsideInline bool) {
|
||||
name := dom.NodeName(node)
|
||||
|
||||
structure := getMarkdownStructure(name)
|
||||
if (structure == "container_block" || structure == "leaf_block") && (isInsideLeafBlock || isInsideInline) {
|
||||
// A block inside an inline OR a block inside a leaf-block
|
||||
// is not valid markdown so cannot be rendered.
|
||||
//
|
||||
// For example, you cannot place a blockquote inside a heading.
|
||||
//
|
||||
// Instead of this weird output (## Heading > My Quote)
|
||||
// we try to find alternatives (## Heading "My Quote")
|
||||
fn, ok := alternatives[name]
|
||||
if ok {
|
||||
fn(ctx, node)
|
||||
} else {
|
||||
node.Data = "span"
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - //
|
||||
|
||||
if structure == "leaf_block" {
|
||||
isInsideLeafBlock = true
|
||||
}
|
||||
if structure == "inline" {
|
||||
isInsideInline = true
|
||||
}
|
||||
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
defer finder(child, isInsideLeafBlock, isInsideInline)
|
||||
}
|
||||
}
|
||||
finder(doc, false, false)
|
||||
}
|
||||
Generated
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func getNextTextNode(startNode *html.Node) *html.Node {
|
||||
node := dom.GetNextNeighborNodeExcludingOwnChild(startNode)
|
||||
|
||||
for node != nil {
|
||||
if node.Type == html.TextNode {
|
||||
return node
|
||||
}
|
||||
|
||||
if dom.NodeName(node) == "span" {
|
||||
// A span has no special meaning. So we just skip it...
|
||||
node = dom.GetNextNeighborNode(node)
|
||||
continue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func getPrevTextNode(startNode *html.Node) *html.Node {
|
||||
node := dom.GetPrevNeighborNodeExcludingOwnChild(startNode)
|
||||
|
||||
for node != nil {
|
||||
if node.Type == html.TextNode {
|
||||
return node
|
||||
}
|
||||
|
||||
if dom.NodeName(node) == "span" {
|
||||
// A span has no special meaning. So we just skip it...
|
||||
node = dom.GetPrevNeighborNode(node)
|
||||
continue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func hasTextChildNodes(startNode *html.Node) bool {
|
||||
var found bool
|
||||
|
||||
var finder func(*html.Node)
|
||||
finder = func(node *html.Node) {
|
||||
if node.Type == html.TextNode && node.Data != "" {
|
||||
found = true
|
||||
return
|
||||
}
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
finder(child)
|
||||
}
|
||||
}
|
||||
finder(startNode)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func RemoveEmptyCode(ctx context.Context, doc *html.Node) {
|
||||
node := doc
|
||||
for node != nil {
|
||||
if dom.NodeName(node) == "code" && !hasTextChildNodes(node) {
|
||||
next := dom.GetNextNeighborNodeExcludingOwnChild(node)
|
||||
|
||||
dom.RemoveNode(node)
|
||||
|
||||
node = next
|
||||
continue
|
||||
}
|
||||
|
||||
node = dom.GetNextNeighborNode(node)
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
var ListEndCommentData = "THE END"
|
||||
|
||||
func AddListEndComments(ctx context.Context, doc *html.Node) {
|
||||
node := doc
|
||||
for node != nil {
|
||||
if nameIsList(node) && nextNameIsList(node) {
|
||||
insertComment(node)
|
||||
}
|
||||
|
||||
node = dom.GetNextNeighborElement(node)
|
||||
}
|
||||
}
|
||||
|
||||
func nameIsList(node *html.Node) bool {
|
||||
name := dom.NodeName(node)
|
||||
return name == "ul" || name == "ol"
|
||||
}
|
||||
|
||||
func insertComment(listNode *html.Node) {
|
||||
comment := &html.Node{
|
||||
Type: html.CommentNode,
|
||||
Data: ListEndCommentData,
|
||||
}
|
||||
listNode.Parent.InsertBefore(comment, listNode.NextSibling)
|
||||
}
|
||||
|
||||
func nextNameIsList(startNode *html.Node) bool {
|
||||
node := dom.GetNextNeighborNodeExcludingOwnChild(startNode)
|
||||
|
||||
for node != nil {
|
||||
name := dom.NodeName(node)
|
||||
if name == "ul" || name == "ol" {
|
||||
return true
|
||||
}
|
||||
if name == "li" {
|
||||
return false
|
||||
}
|
||||
if name == "#comment" && node.Data == ListEndCommentData {
|
||||
return false
|
||||
}
|
||||
|
||||
// If there is any text between two lists
|
||||
// they are automatically not connected anymore.
|
||||
if node.Type == html.TextNode {
|
||||
return false
|
||||
}
|
||||
|
||||
// - - - - //
|
||||
|
||||
if name == "hr" {
|
||||
// A divider already seperates two lists...
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: RunContext.Render()
|
||||
// -> get acess to keepRemoveMap
|
||||
|
||||
// TODO: look in the KeepRemoveMap?
|
||||
// e.g. ul then script then ul
|
||||
|
||||
node = dom.GetNextNeighborNode(node)
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
Generated
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
// MoveListItems moves non-"li" nodes into the previous "li" nodes.
|
||||
func MoveListItems(ctx context.Context, n *html.Node) {
|
||||
if n.Type == html.ElementNode && (n.Data == "ol" || n.Data == "ul") {
|
||||
var previousLi *html.Node
|
||||
|
||||
// Collect children to avoid modifying the slice while iterating.
|
||||
children := dom.AllChildNodes(n)
|
||||
|
||||
for _, child := range children {
|
||||
if child.Type == html.ElementNode && child.Data == "li" {
|
||||
previousLi = child
|
||||
} else if child.Type == html.TextNode && strings.TrimSpace(child.Data) == "" {
|
||||
// Skip the node, probably just formatting of code
|
||||
} else {
|
||||
// We expect that inside an "ol"/"ul" there are *only* "li" nodes.
|
||||
// But sometimes that is not the case...
|
||||
|
||||
if previousLi != nil {
|
||||
// There is a previous "li" node,
|
||||
// so we move this content into the other "li" node.
|
||||
n.RemoveChild(child)
|
||||
|
||||
previousLi.AppendChild(child)
|
||||
} else {
|
||||
// There is no previous "li" node,
|
||||
// so we wrap this node with it's own "li" node.
|
||||
|
||||
newNode := &html.Node{
|
||||
Type: html.ElementNode,
|
||||
DataAtom: atom.Li,
|
||||
Data: "li",
|
||||
}
|
||||
previousLi = dom.WrapNode(child, newNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
MoveListItems(ctx, c)
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func RemoveRedundant(doc *html.Node, matchFn func(*html.Node, *html.Node) bool) {
|
||||
for _, node := range dom.AllNodes(doc) {
|
||||
if hasSameTypeAncestor(node, matchFn) {
|
||||
dom.UnwrapNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func hasSameTypeAncestor(n *html.Node, matchFn func(*html.Node, *html.Node) bool) bool {
|
||||
if !matchFn(n, n) {
|
||||
return false
|
||||
}
|
||||
|
||||
for p := n.Parent; p != nil; p = p.Parent {
|
||||
if matchFn(n, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func isFakeSpan(node *html.Node) bool {
|
||||
name := dom.NodeName(node)
|
||||
if name != "span" {
|
||||
return false
|
||||
}
|
||||
|
||||
var containsBlockNode = false
|
||||
|
||||
var finder func(*html.Node)
|
||||
finder = func(node *html.Node) {
|
||||
name := dom.NodeName(node)
|
||||
if dom.NameIsBlockNode(name) {
|
||||
containsBlockNode = true
|
||||
return
|
||||
}
|
||||
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
finder(child)
|
||||
}
|
||||
}
|
||||
finder(node)
|
||||
|
||||
return containsBlockNode
|
||||
}
|
||||
|
||||
// RenameFakeSpans renames all "span" nodes to "div" if
|
||||
// any block element is found as a child.
|
||||
func RenameFakeSpans(ctx context.Context, doc *html.Node) {
|
||||
var finder func(node *html.Node)
|
||||
finder = func(node *html.Node) {
|
||||
if isFakeSpan(node) {
|
||||
node.Data = "div"
|
||||
}
|
||||
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
finder(child)
|
||||
}
|
||||
}
|
||||
|
||||
finder(doc)
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package domutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/JohannesKaufmann/dom"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func swapTagsOfNodes(node1, node2 *html.Node) {
|
||||
if node1.Type != html.ElementNode || node2.Type != html.ElementNode {
|
||||
panic("swap only works with element nodes")
|
||||
}
|
||||
|
||||
tempDataAtom := node1.DataAtom
|
||||
tempData := node1.Data
|
||||
tempAttr := node1.Attr
|
||||
|
||||
node1.DataAtom = node2.DataAtom
|
||||
node1.Data = node2.Data
|
||||
node1.Attr = node2.Attr
|
||||
|
||||
node2.DataAtom = tempDataAtom
|
||||
node2.Data = tempData
|
||||
node2.Attr = tempAttr
|
||||
}
|
||||
|
||||
func isEmptyText(node *html.Node) bool {
|
||||
return node.Type == html.TextNode && strings.TrimSpace(node.Data) == ""
|
||||
}
|
||||
func SwapTags(ctx context.Context, doc *html.Node, isOuterNode, isInnerNode func(*html.Node) bool) {
|
||||
var finder func(*html.Node)
|
||||
finder = func(node *html.Node) {
|
||||
if isOuterNode(node) {
|
||||
childs := dom.AllChildNodes(node)
|
||||
childs = slices.DeleteFunc(childs, isEmptyText)
|
||||
|
||||
if len(childs) == 1 && isInnerNode(childs[0]) {
|
||||
swapTagsOfNodes(node, childs[0])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
finder(child)
|
||||
}
|
||||
}
|
||||
finder(doc)
|
||||
}
|
||||
Reference in New Issue
Block a user