A stylesheet language written in TypeScript that compiles to CSS
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

compiler.test.ts 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import * as test from 'tape';
  2. import { AST } from '../ast';
  3. import Compiler, { CompilerOpts } from '../compiler';
  4. import Env, { EnvError } from '../env';
  5. import Lexer, { LexerError, LexerResult } from '../lexer';
  6. import Parser, { ParserError, ParserResult } from '../parser';
  7. import Token, { TokenTypes } from '../token';
  8. const defaultOpts = {
  9. prettyPrint: false,
  10. env: new Env(),
  11. };
  12. const compile = (source: string, opts: CompilerOpts = defaultOpts)=> {
  13. const { prettyPrint, env } = opts;
  14. const lexerResult: LexerResult = new Lexer(source).scan();
  15. if (!(lexerResult instanceof LexerError)) {
  16. const parserResult: ParserResult = new Parser(lexerResult).parse();
  17. if (!(parserResult instanceof ParserError)) {
  18. return new Compiler(parserResult, { prettyPrint, env }).compile();
  19. }
  20. return parserResult;
  21. }
  22. return lexerResult;
  23. };
  24. test('compiles literal', (t) => {
  25. t.plan(1);
  26. const result = compile('#FF0000');
  27. t.equal(result, '#FF0000');
  28. });
  29. test('compiles expression', (t) => {
  30. t.plan(1);
  31. const result = compile('(div :color blue)');
  32. t.equal(result, 'div{color:blue;}');
  33. });
  34. test('compiles expression with pretty printing', (t) => {
  35. t.plan(1);
  36. const result = compile('(div :color blue)', { prettyPrint: true });
  37. t.equal(result, 'div {\n color: blue;\n}');
  38. });
  39. test('compiles nested expression', (t) => {
  40. t.plan(1);
  41. const result = compile('(div :color blue (span :color red))');
  42. t.equal(result, 'div{color:blue;}div span{color:red;}');
  43. });
  44. test('omits empty sets', (t) => {
  45. t.plan(1);
  46. const result = compile('(div (span :color blue))');
  47. t.equal(result, 'div span{color:blue;}');
  48. });
  49. test('pretty printing separates multiple selectors', (t) => {
  50. t.plan(1);
  51. const result = compile('(div, p :color blue)', { prettyPrint: true });
  52. t.equal(result, 'div, p {\n color: blue;\n}');
  53. });
  54. test('compile identifier', (t) => {
  55. t.plan(1);
  56. const result = compile('(@let $red #FF0000 (div :color $red))');
  57. t.equal(result, 'div{color:#FF0000;}');
  58. });
  59. test('undefined variable throws an error', (t) => {
  60. t.plan(1);
  61. const result = compile('(@let $red #FF0000)(div :color $blue)');
  62. t.true(result instanceof EnvError);
  63. });
  64. test('pretty printing does not return an extra newline at the end', (t) => {
  65. t.plan(1);
  66. const result = compile('(div :color blue)', { prettyPrint: true });
  67. t.equal(result, 'div {\n color: blue;\n}');
  68. });
  69. test('compiles media query', (t) => {
  70. t.plan(1);
  71. const result = compile(
  72. '(@media :min-width 1000px (div :flex-direction row))'
  73. );
  74. t.equal(result, '@media(min-width:1000px){div{flex-direction:row;}}');
  75. });
  76. test('compiles pretty media query', (t) => {
  77. t.plan(1);
  78. const result = compile(
  79. '(@media :min-width 1000px (div :flex-direction row))',
  80. { prettyPrint: true }
  81. );
  82. t.equal(
  83. result,
  84. '@media(min-width: 1000px) {\n div {\n flex-direction: row;\n }\n}'
  85. );
  86. });
  87. test('compiles keyframe animation', (t) => {
  88. t.plan(1);
  89. const result = compile('(@keyframes fade (0% :opacity 1) (100% :opacity 2))');
  90. t.equal(result, '@keyframes fade{0%{opacity:1;}100%{opacity:2;}}');
  91. });
  92. test('compiles pretty keyframe animation', (t) => {
  93. t.plan(1);
  94. const result = compile(
  95. '(@keyframes fade (0% :opacity 1) (100% :opacity 2))',
  96. { prettyPrint: true }
  97. );
  98. t.equal(
  99. result,
  100. '@keyframes fade {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 2;\n }\n}'
  101. );
  102. });
  103. test('does not append extra semicolons', (t) => {
  104. t.plan(1);
  105. const result = compile('(div :color blue :font-size 16px :display flex)');
  106. t.equal(result, 'div{color:blue;font-size:16px;display:flex;}');
  107. });
  108. test('deeply nested rules', (t) => {
  109. t.plan(1);
  110. const result = compile('(body (main (div (ul (li (a :color pink))))))');
  111. t.equal(result, 'body main div ul li a{color:pink;}');
  112. });
  113. test('compile function applications', (t) => {
  114. t.plan(2);
  115. t.equal(compile('(@rgba 1 2 3 4)'), 'rgba(1,2,3,4)');
  116. t.equal(compile('(div :color (@rgba 1 2 3 4))'), 'div{color:rgba(1,2,3,4);}');
  117. });
  118. test('identifiers in function applications', (t) => {
  119. t.plan(1);
  120. t.equal(compile('(@let $red 30 $green 40 $blue 50 (div :color (@rgb $red $green $blue)))'), 'div{color:rgb(30,40,50);}');
  121. });
  122. test('ampersand refers to parent selector', (t) => {
  123. t.plan(1);
  124. t.equal(compile('(div :color green (&:hover :color blue))'), 'div{color:green;}div:hover{color:blue;}');
  125. });
  126. test('compiles mixins', (t) => {
  127. t.plan(2);
  128. const env = new Env();
  129. const result = compile('(@mixin centered ($width) :max-width $width :margin auto)', {
  130. env: env,
  131. });
  132. t.equal(result, '');
  133. t.deepEqual(env.get(new Token(TokenTypes.LITERAL, 'centered', 1)), new AST.Mixin(
  134. new Token(TokenTypes.LITERAL, 'centered', 1),
  135. [new AST.Identifier(new Token(TokenTypes.IDENTIFIER, 'width', 1))],
  136. [
  137. new AST.Rule(
  138. new AST.Property(new Token(TokenTypes.PROPERTY, 'max-width', 1)),
  139. new AST.Identifier(new Token(TokenTypes.IDENTIFIER, 'width', 1)),
  140. ),
  141. new AST.Rule(
  142. new AST.Property(new Token(TokenTypes.PROPERTY, 'margin', 1)),
  143. new AST.Literal(new Token(TokenTypes.LITERAL, 'auto', 1)),
  144. )
  145. ]
  146. ));
  147. });