Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
brainfood
/
astro-wt
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
2b0bc0ee
authored
2024-06-05 15:33:17 -0500
by
Adam Heath
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Many updates, working on adding typescript, and direct node rendering.
1 parent
2faeafaa
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
95 additions
and
65 deletions
lib/api.ts
lib/astro.ts
lib/children.astro
lib/custom.astro
lib/html.ts
lib/render.astro → lib/match.astro
lib/node.astro
lib/replace.astro
src/layouts/base.astro
lib/api.ts
View file @
2b0bc0e
...
...
@@ -2,7 +2,12 @@ import { configureStore } from '@reduxjs/toolkit'
import
{
createSlice
}
from
'@reduxjs/toolkit'
import
{
createApi
,
fetchBaseQuery
}
from
'@reduxjs/toolkit/query'
import
{
parseHtml
}
from
'./html.js'
import
{
parseHtml
}
from
'./html.ts'
interface
SiteConfig
{
name
:
string
,
baseUrl
:
string
,
}
export
const
configSlice
=
createSlice
({
name
:
'config'
,
...
...
@@ -30,7 +35,7 @@ const siteBaseQuery = async (args, api, options) => {
}
export
const
sitePageSlice
=
createApi
({
reducerPath
:
'
moqui
'
,
reducerPath
:
'
pages
'
,
tagTypes
:
[
'Page'
],
keepUnusedDataFor
:
60
,
refetchOnReconnect
:
true
,
...
...
@@ -65,17 +70,16 @@ export const store = configureStore({
]),
})
export
const
getSitePage
=
async
(
site
,
page
)
=>
{
export
const
getSitePage
=
async
(
site
:
string
,
page
:
string
)
=>
{
const
result
=
await
store
.
dispatch
(
sitePageSlice
.
endpoints
.
getPage
.
initiate
({
site
,
page
}))
if
(
result
.
data
)
result
.
doc
=
parseHtml
(
result
.
data
)
return
result
}
export
const
setSiteConfig
=
(
site
Def
)
=>
{
return
store
.
dispatch
(
configSlice
.
actions
.
setSiteConfig
(
site
Def
))
export
const
setSiteConfig
=
(
site
Config
:
SiteConfig
)
=>
{
return
store
.
dispatch
(
configSlice
.
actions
.
setSiteConfig
(
site
Config
))
}
export
const
getSiteBaseUrl
=
(
name
)
=>
{
export
const
getSiteBaseUrl
=
(
name
:
string
):
string
=>
{
return
configSlice
.
selectors
.
getSiteBaseUrl
(
store
.
getState
(),
name
)
}
...
...
lib/astro.ts
View file @
2b0bc0e
import
Children
from
'./children.astro'
import
Custom
from
'./custom.astro'
import
Render
from
'./render.astro'
import
Match
from
'./match.astro'
import
Node
from
'./node.astro'
import
Replace
from
'./replace.astro'
export
{
Children
,
Custom
,
Render
,
Replace
}
export
{
Children
,
Custom
,
Match
,
Node
,
Replace
}
...
...
lib/children.astro
View file @
2b0bc0e
---
import
Render, { slotPassThrough } from './render
.astro'
import
Match from './match
.astro'
const { props: { debug, parent, children, replacers, slotHandler } } = Astro
const slotCallback = slotPassThrough(Astro)
const { props: { parent, children, debug = false, replacers, slotHandler } } = Astro
//console.log('Children:render', { parent, children, replacers })
---
{
Array.isArray(children) ?
children.map((child, index) => <
Render debug={debug} parent={parent} node={child} index={index
} replacers={replacers} slotHandler={slotHandler}/>)
children.map((child, index) => <
Match parent={parent} node={child} index={index} debug={debug
} replacers={replacers} slotHandler={slotHandler}/>)
: !children ? ''
: <
Render debug={debug} parent={parent} node={children} index={0
} replacers={replacers} slotHandler={slotHandler}/>
: <
Match parent={parent} node={children} index={0} debug={debug
} replacers={replacers} slotHandler={slotHandler}/>
}
...
...
lib/custom.astro
View file @
2b0bc0e
---
import { slotPassThrough } from './render.astro'
import Children from './children.astro'
const { props: { wrap = false, wrapAttributes = {}, node, replacers, slotHandler } } = Astro
const { name: Name, attributes, children } = node
const CustomName = `custom-${Name}`
const slotCallback = slotPassThrough(Astro)
//console.log('Got custom match', node)
---
...
...
lib/html.ts
View file @
2b0bc0e
import
nodeUtil
from
'util'
import
NodeCache
from
'node-cache'
import
type
{
NodeType
}
from
'ultrahtml'
import
{
parse
as
umParse
,
render
as
umRender
,
transform
as
umTransform
,
__unsafeHTML
,
parse
as
ultraParse
,
ELEMENT_NODE
,
TEXT_NODE
,
walkSync
,
}
from
'ultrahtml'
import
type
{
AST
}
from
'parsel-js'
import
{
parse
as
elParse
,
specificity
as
getSpecificity
,
...
...
@@ -19,21 +18,37 @@ import {
import
{
parsedHtmlCache
,
selectorCache
,
findNodeCache
}
from
'./cache.js'
export
const
fixAttributes
=
(
attributes
,
options
=
{
mapClassname
:
true
})
=>
{
const
{
mapClassname
}
=
options
if
(
!
mapClassname
)
return
attributes
const
{
class
:
className
,
...
rest
}
=
attributes
return
{
...
rest
,
className
}
}
export
const
appendClasses
=
(
node
,
extraClasses
)
=>
{
if
(
!
node
.
attributes
.
class
&&
!
extraClasses
)
return
node
const
{
attributes
:
{
class
:
className
,
...
attributes
}
=
{}
}
=
node
return
{
...
node
,
attributes
:
{
...
attributes
,
class
:
`
${
className
?
className
+
' '
:
''
}${
extraClasses
}
`
}
}
}
// TODO: implement a parent/child/element cache
const
filterChildElements
=
(
parent
)
=>
parent
?.
children
?.
filter
(
n
=>
n
.
type
===
ELEMENT_NODE
)
||
[]
const
nthChildPos
=
(
node
,
parent
)
=>
filterChildElements
(
parent
).
findIndex
((
child
)
=>
child
===
node
);
const
makeNthChildPosMatcher
=
(
ast
)
=>
{
type
Matcher
=
(
context
,
node
,
parent
,
i
,
debug
)
=>
boolean
type
AttrValueMatch
=
(
string
)
=>
string
const
makeNthChildPosMatcher
=
(
ast
:
AST
):
Matcher
=>
{
const
{
argument
}
=
ast
const
n
=
Number
(
argument
)
if
(
!
Number
.
isNaN
(
n
))
{
return
(
context
,
node
,
parent
,
i
)
=>
{
return
(
context
,
node
,
parent
,
i
,
debug
)
=>
{
return
i
===
n
}
}
switch
(
argument
)
{
case
'odd'
:
return
(
context
,
node
,
parent
,
i
)
=>
Math
.
abs
(
i
%
2
)
===
1
return
(
context
,
node
,
parent
,
i
,
debug
)
=>
Math
.
abs
(
i
%
2
)
===
1
case
'even'
:
return
(
context
,
node
,
parent
,
i
)
=>
i
%
2
===
0
default
:
{
...
...
@@ -56,7 +71,7 @@ const makeNthChildPosMatcher = (ast) => {
}
}
const
getAttrValueMatch
=
(
value
,
operator
=
'='
,
caseSenstive
)
=>
{
const
getAttrValueMatch
=
(
value
:
string
,
operator
:
string
=
'='
,
caseSenstive
:
boolean
):
AttrValueMatch
=>
{
if
(
value
===
undefined
)
return
(
attrValue
)
=>
attrValue
!==
undefined
const
isCaseInsenstive
=
caseSensitive
===
'i'
if
(
isCaseInsensitive
)
value
=
value
.
toLowerCase
()
...
...
@@ -78,11 +93,11 @@ const getAttrValueMatch = (value, operator = '=', caseSenstive) => {
return
(
attrValue
)
=>
false
}
const
compileMatcher
=
(
ast
,
selector
)
=>
{
const
compileMatcher
=
(
ast
:
AST
,
selector
:
string
):
Matcher
=>
{
let
counter
=
0
const
neededContext
=
[]
const
makeMatcher
=
(
ast
)
=>
{
const
makeMatcher
=
(
ast
:
AST
)
=>
{
//console.log('makeMatcher', ast)
switch
(
ast
.
type
)
{
case
'list'
:
{
...
...
@@ -206,7 +221,7 @@ const compileMatcher = (ast, selector) => {
const
nthChildMatcher
=
makeNthChildPosMatcher
(
ast
)
return
(
context
,
node
,
parent
,
i
,
debug
)
=>
{
const
pos
=
nthChildPos
(
node
,
parent
)
+
1
return
nthChildMatcher
(
context
,
node
,
parent
,
pos
)
return
nthChildMatcher
(
context
,
node
,
parent
,
pos
,
debug
)
}
}
default
:
...
...
@@ -239,25 +254,25 @@ const compileMatcher = (ast, selector) => {
}
}
export
const
createMatcher
=
(
selector
)
=>
{
export
const
createMatcher
=
(
selector
:
string
)
=>
{
const
matcherCreater
=
selectorCache
.
get
(
selector
)
if
(
false
&&
matcher
)
return
matcherCreater
()
if
(
false
&&
matcher
Creater
)
return
matcherCreater
()
const
ast
=
elParse
(
selector
)
//
console.log('createMatcher', nodeUtil.inspect({ selector, ast }, { depth: null, colors: true }))
console
.
log
(
'createMatcher'
,
nodeUtil
.
inspect
({
selector
,
ast
},
{
depth
:
null
,
colors
:
true
}))
const
newMatcherCreater
=
compileMatcher
(
ast
,
selector
)
selectorCache
.
set
(
selector
,
newMatcherCreater
)
return
newMatcherCreater
()
}
export
const
parseHtml
=
(
html
)
=>
{
export
const
parseHtml
=
(
html
:
string
)
=>
{
const
cached
=
parsedHtmlCache
.
get
(
html
)
if
(
cached
)
return
cached
const
doc
=
u
m
Parse
(
html
)
const
doc
=
u
ltra
Parse
(
html
)
parsedHtmlCache
.
set
(
html
,
doc
)
return
doc
}
export
const
findNode
=
(
doc
,
selector
)
=>
{
export
const
findNode
=
(
doc
:
NodeType
,
selector
:
string
)
=>
{
if
(
!
selector
)
return
doc
let
docCache
=
findNodeCache
.
get
(
doc
)
if
(
!
docCache
)
{
...
...
lib/
render
.astro
→
lib/
match
.astro
View file @
2b0bc0e
---
import { ELEMENT_NODE, TEXT_NODE } from 'ultrahtml'
import
Children from './children
.astro'
import
Node from './node
.astro'
export const slotPassThrough = (Astro) => (slotName, node) => {
console.log('calling slot', { slotName, node, Astro })
return Astro.slots.render('default', [ slotName, node ])
}
const { props: { debug = false, parent = null, node, index = 0, replacers, slotHandler } } = Astro
const { props: { parent = null, node, index = 0, debug = false, replacers, slotHandler } } = Astro
const { name: Name, attributes } = node
if (debug) {
...
...
@@ -22,17 +17,8 @@ for (const [ matcher, handler ] of replacers) {
}
}
//const slotCallback = slotPassThrough(Astro)
---
{
slotName ? slotHandler(slotName, node)
: node.type === TEXT_NODE ? <Fragment set:html={node.value}/>
: node.type === ELEMENT_NODE ? (
node.isSelfClosingTag ? <Name {...attributes}/>
: <Name {...attributes}>
<Children parent={node} children={node.children} replacers={replacers} slotHandler={slotHandler}/>
</Name>
) : (
<Children parent={node} children={node.children} replacers={replacers} slotHandler={slotHandler}/>
)
: <Node parent={parent} node={node} index={index} debug={debug} replacers={replacers} slotHandler={slotHandler}/>
}
...
...
lib/node.astro
0 → 100644
View file @
2b0bc0e
---
import { ELEMENT_NODE, TEXT_NODE } from 'ultrahtml'
import Children from './children.astro'
const { props: { parent = null, node, index = 0, debug = false, replacers, slotHandler } } = Astro
const { name: Name, attributes } = node
if (debug) {
console.log('Node.astro:debug', {node})
}
---
{
node.type === TEXT_NODE ? <Fragment set:html={node.value}/>
: node.type === ELEMENT_NODE ? (
node.isSelfClosingTag ? <Name {...attributes}/>
: <Name {...attributes}>
<Children parent={node} children={node.children} debug={debug} replacers={replacers} slotHandler={slotHandler}/>
</Name>
) : (
<Children parent={node} children={node.children} debug={debug} replacers={replacers} slotHandler={slotHandler}/>
)
}
lib/replace.astro
View file @
2b0bc0e
---
import html from '@resources/provider-portal.html?raw'
import { walkSync } from 'ultrahtml'
import { parseHtml, createMatcher, findNode } from './html.
j
s'
import
Render from './render
.astro'
import { parseHtml, createMatcher, findNode } from './html.
t
s'
import
Match from './match
.astro'
const { props } = Astro
const { html,
mapClassname = tru
e, xpath, replacements = {} } = props
const { html,
debug = fals
e, xpath, replacements = {} } = props
const doc = props.node ? props.node : parseHtml(props.html)
const node = xpath ? findNode(doc, xpath) : doc
const replacers = Object.entries(replacements).map(([ selector, handler ]) => [ createMatcher(selector), handler ])
const fixAttributes = ({ attributes }) => {
if (!mapClassname) return attributes
const { class: className, ...rest } = attributes
return { ...rest, className }
}
const slotHandler = (slotName, node) => {
const attributes = fixAttributes(node)
return Astro.slots.render(slotName, [ { ...node, attributes }, { slotHandler, replacers } ] )
return Astro.slots.render(slotName, [ node, { slotHandler, replacers } ] )
}
---
<
Render node={node
} replacers={replacers} slotHandler={slotHandler}/>
<
Match node={node} debug={debug
} replacers={replacers} slotHandler={slotHandler}/>
...
...
src/layouts/base.astro
View file @
2b0bc0e
---
import { getSitePage } from '@lib/api.ts'
import { Replace } from '@lib/astro.ts'
import { appendClasses } from '@lib/html.ts'
import { Node, Replace } from '@lib/astro.ts'
import '@config.ts'
const { data: layoutHtml } = await getSitePage('msd', '/')
const layoutReplacements = {
['body']: 'body',
['.hero-section']: 'content',
['.content-section']: 'DELETE',
['.content-section-4']: 'DELETE',
...
...
@@ -14,7 +16,17 @@ const layoutReplacements = {
//['.navbar.w-nav:nth-of-type(n + 1)']: 'DELETE',
}
---
<Replace
debug={true}
html={layoutHtml} replacements={layoutReplacements}>
<Replace html={layoutHtml} replacements={layoutReplacements}>
<define slot="DELETE">{(node) => ''}</define>
<define slot='content'>{(node) => <slot/>}</define>
<define slot='body'>{(node, context) => <Node {...context} node={appendClasses(node, 'astro')} parent={node.parent}/>}</define>
<define slot='content'>{(node) => <div><slot/></div>}</define>
</Replace>
<style>
.astro {}
a.w-webflow-badge {
display:none;
}
:global(a.w-webflow-badge) {
display:none;
}
</style>
...
...
Please
register
or
sign in
to post a comment