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
|
import { EvalActions, evalExprAsString } from './eval'
import { Expr, Selector, SelectorComp, parseDeclarations } from './parser'
import { match, matchString } from './utils/adt'
export interface Declaration {
selector: Selector
properties: Map<string, Expr>
isInstance: boolean
}
export interface DeclarationEval {
selector: Selector
properties: Array<readonly [string, string]>
}
export const evaluateDeclaration = async (
{ selector, properties }: Declaration,
actions: EvalActions,
): Promise<DeclarationEval> => {
if (properties.size === 0) return { selector, properties: [] }
const props = await Promise.all(
[...properties.entries()].map(async ([key, expr]) => {
// Ignore errors?
const result = await evalExprAsString(expr, actions).catch((e: any) =>
console.warn(e),
)
return [key, result ?? ''] as const
}),
)
return { selector, properties: props }
}
const instanceCountMap = new Map<string, number>()
const getUniqueInstanceId = (id: string) => {
const instanceCount = instanceCountMap.get(id) ?? 0
instanceCountMap.set(id, instanceCount + 1)
return `${id}--index-${instanceCount}`
}
export const toDeclaration = (expr: Expr): Declaration | undefined => {
let selector: Selector | undefined
const properties: Map<string, Expr> = new Map()
let isInstance = false
match(expr, {
Selector: sel => {
selector = sel
},
Call: ({ name, args }) => {
matchString(name, {
instance: () => {
isInstance = true
const [sel, map] = args
// Selector
match(sel, {
Selector: sel => {
selector = sel
},
_: _ => {},
})
// TODO: Refactor with eval
match(map, {
Call: ({ name, args }) => {
if (name !== 'map') return
for (const arg of args) {
match(arg, {
Pair: ({ key, value }) => properties.set(key, value),
_: _ => {},
})
}
},
})
},
_: () => {
throw new Error(`weird function in cssx-chi9ldren: ${name}`)
},
})
},
_: () => {},
})
if (!selector) return undefined
if (isInstance) {
const baseId = selector.id
selector.id = getUniqueInstanceId(selector.id)
selector.selectors.push(SelectorComp.Attr(['data-instance', baseId]))
}
return { selector, properties, isInstance }
}
export const expressionsToDeclrs = async (
exprs: Array<Expr>,
actions: EvalActions,
): Promise<Array<DeclarationEval>> => {
const declrs = await Promise.all(
exprs
.map(toDeclaration)
.filter(declr => !!declr)
.map(declr => declr && evaluateDeclaration(declr, actions)),
)
return declrs.filter(declr => !!declr) as Array<DeclarationEval>
}
export const extractDeclaration = async (
input: string,
actions: EvalActions,
): Promise<Array<DeclarationEval>> => {
const exprs = parseDeclarations(input)
return expressionsToDeclrs(exprs, actions)
}
|