Declaration.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. var isCustomProperty = require('../../utils/names').isCustomProperty;
  2. var TYPE = require('../../tokenizer').TYPE;
  3. var IDENTIFIER = TYPE.Identifier;
  4. var COLON = TYPE.Colon;
  5. var EXCLAMATIONMARK = TYPE.ExclamationMark;
  6. var SOLIDUS = TYPE.Solidus;
  7. var ASTERISK = TYPE.Asterisk;
  8. var DOLLARSIGN = TYPE.DollarSign;
  9. var HYPHENMINUS = TYPE.HyphenMinus;
  10. var SEMICOLON = TYPE.Semicolon;
  11. var PLUSSIGN = TYPE.PlusSign;
  12. var NUMBERSIGN = TYPE.NumberSign;
  13. function consumeValueRaw(startToken) {
  14. return this.Raw(startToken, EXCLAMATIONMARK, SEMICOLON, false, true);
  15. }
  16. function consumeCustomPropertyRaw(startToken) {
  17. return this.Raw(startToken, EXCLAMATIONMARK, SEMICOLON, false, false);
  18. }
  19. function consumeValue() {
  20. var startValueToken = this.scanner.currentToken;
  21. var value = this.Value();
  22. if (value.type !== 'Raw' &&
  23. this.scanner.eof === false &&
  24. this.scanner.tokenType !== SEMICOLON &&
  25. this.scanner.tokenType !== EXCLAMATIONMARK &&
  26. this.scanner.isBalanceEdge(startValueToken) === false) {
  27. this.scanner.error();
  28. }
  29. return value;
  30. }
  31. module.exports = {
  32. name: 'Declaration',
  33. structure: {
  34. important: [Boolean, String],
  35. property: String,
  36. value: ['Value', 'Raw']
  37. },
  38. parse: function() {
  39. var start = this.scanner.tokenStart;
  40. var startToken = this.scanner.currentToken;
  41. var property = readProperty.call(this);
  42. var customProperty = isCustomProperty(property);
  43. var parseValue = customProperty ? this.parseCustomProperty : this.parseValue;
  44. var consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw;
  45. var important = false;
  46. var value;
  47. this.scanner.skipSC();
  48. this.scanner.eat(COLON);
  49. if (!customProperty) {
  50. this.scanner.skipSC();
  51. }
  52. if (parseValue) {
  53. value = this.parseWithFallback(consumeValue, consumeRaw);
  54. } else {
  55. value = consumeRaw.call(this, this.scanner.currentToken);
  56. }
  57. if (this.scanner.tokenType === EXCLAMATIONMARK) {
  58. important = getImportant(this.scanner);
  59. this.scanner.skipSC();
  60. }
  61. // Do not include semicolon to range per spec
  62. // https://drafts.csswg.org/css-syntax/#declaration-diagram
  63. if (this.scanner.eof === false &&
  64. this.scanner.tokenType !== SEMICOLON &&
  65. this.scanner.isBalanceEdge(startToken) === false) {
  66. this.scanner.error();
  67. }
  68. return {
  69. type: 'Declaration',
  70. loc: this.getLocation(start, this.scanner.tokenStart),
  71. important: important,
  72. property: property,
  73. value: value
  74. };
  75. },
  76. generate: function(node) {
  77. this.chunk(node.property);
  78. this.chunk(':');
  79. this.node(node.value);
  80. if (node.important) {
  81. this.chunk(node.important === true ? '!important' : '!' + node.important);
  82. }
  83. },
  84. walkContext: 'declaration'
  85. };
  86. function readProperty() {
  87. var start = this.scanner.tokenStart;
  88. var prefix = 0;
  89. // hacks
  90. switch (this.scanner.tokenType) {
  91. case ASTERISK:
  92. case DOLLARSIGN:
  93. case PLUSSIGN:
  94. case NUMBERSIGN:
  95. prefix = 1;
  96. break;
  97. // TODO: not sure we should support this hack
  98. case SOLIDUS:
  99. prefix = this.scanner.lookupType(1) === SOLIDUS ? 2 : 1;
  100. break;
  101. }
  102. if (this.scanner.lookupType(prefix) === HYPHENMINUS) {
  103. prefix++;
  104. }
  105. if (prefix) {
  106. this.scanner.skip(prefix);
  107. }
  108. this.scanner.eat(IDENTIFIER);
  109. return this.scanner.substrToCursor(start);
  110. }
  111. // ! ws* important
  112. function getImportant(scanner) {
  113. scanner.eat(EXCLAMATIONMARK);
  114. scanner.skipSC();
  115. var important = scanner.consume(IDENTIFIER);
  116. // store original value in case it differ from `important`
  117. // for better original source restoring and hacks like `!ie` support
  118. return important === 'important' ? true : important;
  119. }