import type { CSSObject, CSSValue } from '../types' import { interleave } from './interleave' export function astish( strings: CSSObject | string | TemplateStringsArray, interpolations: readonly CSSValue[], ): CSSObject[] { return Array.isArray(strings) ? astish$( interleave(strings as TemplateStringsArray, interpolations, (interpolation) => interpolation != null && typeof interpolation != 'boolean' ? (interpolation as unknown as string) : '', ), ) : typeof strings == 'string' ? astish$(strings) : [strings as CSSObject] } // Based on https://github.com/cristianbote/goober/blob/master/src/core/astish.js const newRule = / *(?:(?:([\u0080-\uFFFF\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}))/g /** * Convert a css style string into a object */ function astish$(css: string): CSSObject[] { css = removeComments(css) const tree: CSSObject[] = [{}] const rules: CSSObject[] = [tree[0]] const conditions: string[] = [] let block: RegExpExecArray | null while ((block = newRule.exec(css))) { // Remove the current entry if (block[4]) { tree.shift() conditions.shift() } if (block[3]) { // new nested conditions.unshift(block[3]) tree.unshift({}) rules.push(conditions.reduce((body, condition) => ({ [condition]: body }), tree[0])) } else if (!block[4]) { // if we already have that property — start a new CSSObject if (tree[0][block[1]]) { tree.unshift({}) rules.push(conditions.reduce((body, condition) => ({ [condition]: body }), tree[0])) } tree[0][block[1]] = block[2] } } // console.log(rules) return rules } // Remove comments (multiline and single line) function removeComments(css: string): string { return css.replace(/\/\*[^]*?\*\/|\s\s+|\n/gm, ' ') }