Atrule.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. var TYPE = require('../../tokenizer').TYPE;
  2. var rawMode = require('./Raw').mode;
  3. var ATKEYWORD = TYPE.AtKeyword;
  4. var SEMICOLON = TYPE.Semicolon;
  5. var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
  6. var RIGHTCURLYBRACKET = TYPE.RightCurlyBracket;
  7. function consumeRaw(startToken) {
  8. return this.Raw(startToken, rawMode.leftCurlyBracketOrSemicolon, true);
  9. }
  10. function isDeclarationBlockAtrule() {
  11. for (var offset = 1, type; type = this.scanner.lookupType(offset); offset++) {
  12. if (type === RIGHTCURLYBRACKET) {
  13. return true;
  14. }
  15. if (type === LEFTCURLYBRACKET ||
  16. type === ATKEYWORD) {
  17. return false;
  18. }
  19. }
  20. return false;
  21. }
  22. module.exports = {
  23. name: 'Atrule',
  24. structure: {
  25. name: String,
  26. prelude: ['AtrulePrelude', 'Raw', null],
  27. block: ['Block', null]
  28. },
  29. parse: function() {
  30. var start = this.scanner.tokenStart;
  31. var name;
  32. var nameLowerCase;
  33. var prelude = null;
  34. var block = null;
  35. this.eat(ATKEYWORD);
  36. name = this.scanner.substrToCursor(start + 1);
  37. nameLowerCase = name.toLowerCase();
  38. this.scanner.skipSC();
  39. // parse prelude
  40. if (this.scanner.eof === false &&
  41. this.scanner.tokenType !== LEFTCURLYBRACKET &&
  42. this.scanner.tokenType !== SEMICOLON) {
  43. if (this.parseAtrulePrelude) {
  44. prelude = this.parseWithFallback(this.AtrulePrelude.bind(this, name), consumeRaw);
  45. // turn empty AtrulePrelude into null
  46. if (prelude.type === 'AtrulePrelude' && prelude.children.head === null) {
  47. prelude = null;
  48. }
  49. } else {
  50. prelude = consumeRaw.call(this, this.scanner.tokenIndex);
  51. }
  52. this.scanner.skipSC();
  53. }
  54. switch (this.scanner.tokenType) {
  55. case SEMICOLON:
  56. this.scanner.next();
  57. break;
  58. case LEFTCURLYBRACKET:
  59. if (this.atrule.hasOwnProperty(nameLowerCase) &&
  60. typeof this.atrule[nameLowerCase].block === 'function') {
  61. block = this.atrule[nameLowerCase].block.call(this);
  62. } else {
  63. // TODO: should consume block content as Raw?
  64. block = this.Block(isDeclarationBlockAtrule.call(this));
  65. }
  66. break;
  67. }
  68. return {
  69. type: 'Atrule',
  70. loc: this.getLocation(start, this.scanner.tokenStart),
  71. name: name,
  72. prelude: prelude,
  73. block: block
  74. };
  75. },
  76. generate: function(node) {
  77. this.chunk('@');
  78. this.chunk(node.name);
  79. if (node.prelude !== null) {
  80. this.chunk(' ');
  81. this.node(node.prelude);
  82. }
  83. if (node.block) {
  84. this.node(node.block);
  85. } else {
  86. this.chunk(';');
  87. }
  88. },
  89. walkContext: 'atrule'
  90. };