import { walk, WalkAction, type AstNode } from './ast'
import { compileCandidates } from './compile'
import type { DesignSystem } from './design-system'
import { escape } from './utils/escape'
export function substituteAtApply(ast: AstNode[], designSystem: DesignSystem) {
walk(ast, (node, { replaceWith }) => {
if (node.kind !== 'at-rule') return
if (node.name === '@keyframes') {
walk(node.nodes, (child) => {
if (child.kind === 'at-rule' && child.name === '@apply') {
throw new Error(`You cannot use \`@apply\` inside \`@keyframes\`.`)
}
})
return WalkAction.Skip
}
if (node.name !== '@apply') return
let candidates = node.params.split(/\s+/g)
{
let candidateAst = compileCandidates(candidates, designSystem, {
onInvalidCandidate: (candidate) => {
throw new Error(`Cannot apply unknown utility class: ${candidate}`)
},
}).astNodes
let newNodes: AstNode[] = []
for (let candidateNode of candidateAst) {
if (candidateNode.kind === 'rule') {
for (let child of candidateNode.nodes) {
newNodes.push(child)
}
} else {
newNodes.push(candidateNode)
}
}
walk(newNodes, (child) => {
if (child !== node) return
for (let candidate of candidates) {
let selector = `.${escape(candidate)}`
for (let rule of candidateAst) {
if (rule.kind !== 'rule') continue
if (rule.selector !== selector) continue
walk(rule.nodes, (child) => {
if (child !== node) return
throw new Error(
`You cannot \`@apply\` the \`${candidate}\` utility here because it creates a circular dependency.`,
)
})
}
}
})
replaceWith(newNodes)
}
})
}