@chasemoskal/magical
Version:
web toolkit for lit apps
88 lines (74 loc) • 2.13 kB
text/typescript
import {Token} from "./types.js"
import {Expression} from "../../types.js"
import {CamelCssMissingClosingBraceError, setupTracedErrors} from "../../errors.js"
export function* parse(tokens: Iterable<Token.Any>) {
type StackFrame = {
selector: undefined | string
ruleName: undefined | string
rules: {[key: string]: string}
childFrames: StackFrame[]
}
let frame: undefined | StackFrame
const stack: StackFrame[] = []
for (const token of tokens) {
const error = setupTracedErrors(token.trace)
switch (token.type) {
case Token.Type.Open:
case Token.Type.AtRule: {
frame = {
selector: (token.type === Token.Type.AtRule)
? token.directive
: token.selector.replaceAll(/\s+/gm, " "),
ruleName: undefined,
rules: {},
childFrames: [],
}
stack.push(frame)
} break
case Token.Type.RuleName: {
if (!frame)
throw error.ruleNamePlacement(token.name)
frame.ruleName = token.name
} break
case Token.Type.RuleValue: {
if (!frame || !frame.ruleName)
throw error.ruleValuePlacement(token.value)
frame.rules[frame.ruleName] = token.value
frame.ruleName = undefined
} break
case Token.Type.Close: {
const completedFrame = stack.pop()
const parentFrame = stack.length > 0
? stack[stack.length - 1]
: undefined
frame = parentFrame
if (!completedFrame)
throw error.excessClosingBrace()
if (!completedFrame.selector)
throw error.missingSelectorOrAtRule()
if (parentFrame)
parentFrame.childFrames.push(completedFrame)
else {
function recursiveFrameToExpression(frame: StackFrame): Expression {
if (frame.selector!.startsWith("@")) {
return [
frame.selector!,
frame.childFrames.map(recursiveFrameToExpression),
]
}
else {
return [
frame.selector!,
frame.rules,
frame.childFrames.map(recursiveFrameToExpression),
]
}
}
yield recursiveFrameToExpression(completedFrame)
}
} break
}
}
if (stack.length > 0)
throw new CamelCssMissingClosingBraceError(stack.length)
}