import * as AST from '../ast'; import Env, { EnvError } from '../env'; export class RuleSet { public selectors: AST.Selector[]; public children: AST.Child[]; public constructor(selectors: AST.Selector[], children: AST.Child[] = []) { this.selectors = selectors; this.children = children; } public compile(env: Env, opts: AST.Opts): string | EnvError { const lineSpacer = opts.prettyPrint ? '\n' : ''; const wordSpacer = opts.prettyPrint ? ' ' : ''; const indentSpacer = opts.prettyPrint ? Array(opts.depth).fill(' ').join('') : ''; const [lBrace, rBrace] = ['{', '}']; const lineages = ([] as string[]).concat( ...this.selectors.map((sel) => sel.getLineages()) ); const rules: (String | EnvError)[] = []; const children: (String | EnvError)[] = []; for (const child of this.children) { if (child instanceof AST.Rule) { rules.push(child.compile(env, opts)); } else if (child instanceof RuleSet) { children.push(child.compile(env, opts)); } else if (child instanceof AST.Application) { const mixin = env.get(child.name.value); if (mixin instanceof EnvError) return mixin; if (mixin instanceof AST.Mixin) { if (mixin.parameters.length !== child.arguments.length) { return new EnvError( child.name.value.line, `Expected ${mixin.parameters.length} arguments but received ${ child.arguments.length }` ); } const mixinEnv = new Env(env); mixin.parameters.forEach((param, index) => { mixinEnv.set(param.name.value, child.arguments[index]); }); mixin.children.forEach((mixinChild) => { if (mixinChild instanceof AST.Rule) { rules.push(mixinChild.compile(mixinEnv, opts)); } else if (mixinChild instanceof RuleSet) { mixinChild.selectors.map((sel) => { sel.parents = this.selectors; }); children.push(mixinChild.compile(mixinEnv, opts)); } else { console.log(mixinChild); } }); } } } const rulesError = rules.find((node) => node instanceof EnvError); if (rulesError instanceof EnvError) return rulesError; const childrenError = children.find((node) => node instanceof EnvError); if (childrenError instanceof EnvError) return childrenError; const compiledRules = rules.join(lineSpacer); const compiledChildren = children.join(lineSpacer); const lineage = lineages.join(`,${wordSpacer}`); const frontMatter = `${indentSpacer}${lineage}${wordSpacer}${lBrace}${lineSpacer}`; const backMatter = `${lineSpacer}${indentSpacer}${rBrace}${ compiledChildren.length ? lineSpacer : '' }`; return rules.length ? `${frontMatter}${compiledRules}${backMatter}${compiledChildren}` : compiledChildren; } }