A stylesheet language written in TypeScript that compiles to CSS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ruleSet.ts 2.9KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import * as AST from '../ast';
  2. import Env, { EnvError } from '../env';
  3. export class RuleSet {
  4. public selectors: AST.Selector[];
  5. public children: AST.Child[];
  6. public constructor(selectors: AST.Selector[], children: AST.Child[] = []) {
  7. this.selectors = selectors;
  8. this.children = children;
  9. }
  10. public compile(env: Env, opts: AST.Opts): string | EnvError {
  11. const lineSpacer = opts.prettyPrint ? '\n' : '';
  12. const wordSpacer = opts.prettyPrint ? ' ' : '';
  13. const indentSpacer = opts.prettyPrint ? Array(opts.depth).fill(' ').join('') : '';
  14. const [lBrace, rBrace] = ['{', '}'];
  15. const lineages = ([] as string[]).concat(
  16. ...this.selectors.map((sel) => sel.getLineages())
  17. );
  18. const rules: (String | EnvError)[] = [];
  19. const children: (String | EnvError)[] = [];
  20. for (const child of this.children) {
  21. if (child instanceof AST.Rule) {
  22. rules.push(child.compile(env, opts));
  23. } else if (child instanceof RuleSet) {
  24. children.push(child.compile(env, opts));
  25. } else if (child instanceof AST.Application) {
  26. const mixin = env.get(child.name.value);
  27. if (mixin instanceof EnvError) return mixin;
  28. if (mixin instanceof AST.Mixin) {
  29. if (mixin.parameters.length !== child.arguments.length) {
  30. return new EnvError(
  31. child.name.value.line,
  32. `Expected ${mixin.parameters.length} arguments but received ${
  33. child.arguments.length
  34. }`
  35. );
  36. }
  37. const mixinEnv = new Env(env);
  38. mixin.parameters.forEach((param, index) => {
  39. mixinEnv.set(param.name.value, child.arguments[index]);
  40. });
  41. mixin.children.forEach((mixinChild) => {
  42. if (mixinChild instanceof AST.Rule) {
  43. rules.push(mixinChild.compile(mixinEnv, opts));
  44. } else if (mixinChild instanceof RuleSet) {
  45. mixinChild.selectors.map((sel) => {
  46. sel.parents = this.selectors;
  47. });
  48. children.push(mixinChild.compile(mixinEnv, opts));
  49. } else {
  50. console.log(mixinChild);
  51. }
  52. });
  53. }
  54. }
  55. }
  56. const rulesError = rules.find((node) => node instanceof EnvError);
  57. if (rulesError instanceof EnvError) return rulesError;
  58. const childrenError = children.find((node) => node instanceof EnvError);
  59. if (childrenError instanceof EnvError) return childrenError;
  60. const compiledRules = rules.join(lineSpacer);
  61. const compiledChildren = children.join(lineSpacer);
  62. const lineage = lineages.join(`,${wordSpacer}`);
  63. const frontMatter = `${indentSpacer}${lineage}${wordSpacer}${lBrace}${lineSpacer}`;
  64. const backMatter = `${lineSpacer}${indentSpacer}${rBrace}${
  65. compiledChildren.length ? lineSpacer : ''
  66. }`;
  67. return rules.length
  68. ? `${frontMatter}${compiledRules}${backMatter}${compiledChildren}`
  69. : compiledChildren;
  70. }
  71. }