react.jsx
5.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import React from 'react'
import parseStyle from 'style-to-object'
import { COMMENT_NODE, DOCUMENT_NODE, DOCTYPE_NODE, ELEMENT_NODE, TEXT_NODE } from 'ultrahtml'
import { parseHtml, createAdjuster, createMatcher, findNode } from 'astro-wt/html'
import { decode } from 'html-entities'
export const ReparseStaticChildren = (replaceOptions, Component) => (props) => {
const { children = {}, ...rest } = props
const { props: { value: staticHtml } = {} } = children
if (!staticHtml) return <Component {...props}/>
const bodyToParse = String(staticHtml)
const ultra = Replace({
...replaceOptions,
html: bodyToParse,
})
return <Component {...rest}>{ultra}</Component>
}
export const fixAttributes = (attributes) => {
const { class: className, style, srcset: srcSet, maxlength: maxLength, for: htmlFor, ...rest } = attributes
const result = rest
if (className !== undefined) result.className = className
if (srcSet !== undefined) result.srcSet = srcSet
if (maxLength !== undefined) result.maxLength = maxLength
if (htmlFor !== undefined) result.htmlFor = htmlFor
if (style) {
const styleObject = parseStyle(style)
result.style = styleObject
}
return result
}
export const CreateReplacement = (Element, propsOrCreator) => (matchOptions) => {
const { debug, parent, node, index, replacers } = matchOptions
const props = propsOrCreator instanceof Function ? propsOrCreator(matchOptions) : propsOrCreator
return (
<Element key={index} {...fixAttributes({...node.attributes, ...props})}>
{Children({ debug, parent, children: node.children, replacers})}
</Element>
)
}
export const DeleteHandler = () => null
export const Replace = (props) => {
const { debug = 0, html, xpath, adjustments, replacements } = props
const adjuster = createAdjuster(adjustments)
const doc = props.node ? props.node : parseHtml(html, { adjuster })
const nodes = xpath ? findNode(doc, xpath, { single: false }) : [ doc ]
const replacers = Object.entries(replacements).map(([ selector, Handler ]) => [ createMatcher(selector), Handler ])
const nextDebug = debug ? debug - 1 : 0
if (debug) console.log('Replace', { html, replacers, node })
const result = nodes.map(node => Match({ debug: nextDebug, node, replacers }))
if (debug) console.log('Replace result', result)
return result
}
export const Match = (props) => {
const { debug = 0, parent = null, node, index = 0, replacers } = props
const nextDebug = debug ? debug - 1 : 0
for (const [ matcher, Handler ] of replacers) {
if (matcher(node, parent, index, false)) {
return Handler ? Handler({ debug: nextDebug, parent, node, index, replacers }) : null
}
}
if (debug) console.log('Match:nothing', {node})
return Node({ debug: nextDebug, parent, node, index, replacers })
}
export const RAW_TAGS = new Set(['script', 'style'])
export const Node = (props) => {
const { debug = 0, parent, node, index, replacers } = props
const { name: Name, attributes } = node
const nextDebug = debug ? debug - 1 : 0
if (node.type === DOCTYPE_NODE) {
return ''
} else if (node.type === DOCUMENT_NODE) {
return Children({ debug: nextDebug, parent: node, children: node.children, replacers })
} else if (node.type === COMMENT_NODE) {
return '<!-- ' + node.value + ' -->'
} else if (node.type === TEXT_NODE) {
if (debug) console.log('Node:text', { value: node.value })
return node.value ? decode(node.value) : null
//return <React.Fragment dangerouslySetInnerHTML={{ __html: node.value }}/>
} else if (node.type === ELEMENT_NODE) {
const elementProps = fixAttributes(attributes)
if (node.isSelfClosingTag) {
if (debug) console.log('Node:selfClosing', { node })
return <Name key={index} {...elementProps}/>
} else {
if (debug) console.log('Node:element', { node, children: node.children })
let children = node.children
if (children.length === 1) {
const [ firstChild ] = children
if (firstChild.type === TEXT_NODE && !firstChild.value) children = null
}
if (Name === 'astro-island') console.log('astro-island', { attributes, elementProps })
if (Name === 'script') console.log('script', { node, children })
if (RAW_TAGS.has(Name) && children) {
if (Array.isArray(children) && children.length === 1) {
return <Name key={index} {...elementProps} dangerouslySetInnerHTML={{ __html: children[ 0 ].value } }/>
}
}
return (
<Name key={index} {...elementProps} children={children && Children({ debug: nextDebug, parent: node, children: node.children, replacers})}/>
)
}
} else {
if (debug) console.log('Node:children', { children: node.children })
return Children({ debug: nextDebug, parent: node, children: node.children, replacers })
}
}
export const Children = (props) => {
const { debug = 0, parent, children, replacers } = props
const nextDebug = debug ? debug - 1 : 0
if (Array.isArray(children)) {
const result = children.map((child, index) => {
return Match({ debug: nextDebug, parent, node: child, index, replacers })
})
if (result.length === 1) return result[0]
if (result.length === 0) return null
return result
} else if (!children) {
return ''
} else {
return Node({ debug: nextDebug, parent, node: children, index: 0, replacers })
}
}