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.

167 lines
3.5 KiB

4 years ago
  1. "use strict";
  2. var Parser = require("fastparse");
  3. function commentMatch(match, content) {
  4. this.value.nodes.push({
  5. type: "comment",
  6. content: content
  7. });
  8. }
  9. function spacingMatch(match) {
  10. var item = this.value.nodes[this.value.nodes.length - 1];
  11. item.after = (item.after || "") + match;
  12. }
  13. function initialSpacingMatch(match) {
  14. this.value.before = match;
  15. }
  16. function endSpacingMatch(match) {
  17. this.value.after = match;
  18. }
  19. function unescapeString(content) {
  20. return content.replace(/\\(?:([a-fA-F0-9]{1,6})|(.))/g, function(all, unicode, otherCharacter) {
  21. if (otherCharacter) {
  22. return otherCharacter;
  23. }
  24. var C = parseInt(unicode, 16);
  25. if(C < 0x10000) {
  26. return String.fromCharCode(C);
  27. } else {
  28. return String.fromCharCode(Math.floor((C - 0x10000) / 0x400) + 0xD800) +
  29. String.fromCharCode((C - 0x10000) % 0x400 + 0xDC00);
  30. }
  31. });
  32. }
  33. function stringMatch(match, content) {
  34. var value = unescapeString(content);
  35. this.value.nodes.push({
  36. type: "string",
  37. value: value,
  38. stringType: match[0]
  39. });
  40. }
  41. function commaMatch(match, spacing) {
  42. var newValue = {
  43. type: "value",
  44. nodes: []
  45. };
  46. if(spacing) {
  47. newValue.before = spacing;
  48. }
  49. this.root.nodes.push(newValue);
  50. this.value = newValue;
  51. }
  52. function itemMatch(match) {
  53. this.value.nodes.push({
  54. type: "item",
  55. name: match
  56. });
  57. }
  58. function nestedItemMatch(match, name, spacing) {
  59. this.stack.push(this.root);
  60. this.root = {
  61. type: "nested-item",
  62. name: name,
  63. nodes: [
  64. { type: "value", nodes: [] }
  65. ]
  66. };
  67. if(spacing) {
  68. this.root.nodes[0].before = spacing;
  69. }
  70. this.value.nodes.push(this.root);
  71. this.value = this.root.nodes[0];
  72. }
  73. function nestedItemEndMatch(match, spacing, remaining) {
  74. if(this.stack.length === 0) {
  75. if(spacing) {
  76. var item = this.value.nodes[this.value.nodes.length - 1];
  77. item.after = (item.after || "") + spacing;
  78. }
  79. this.value.nodes.push({
  80. type: "invalid",
  81. value: remaining
  82. });
  83. } else {
  84. if(spacing) {
  85. this.value.after = spacing;
  86. }
  87. this.root = this.stack.pop();
  88. this.value = this.root.nodes[this.root.nodes.length - 1];
  89. }
  90. }
  91. function urlMatch(match, innerSpacingBefore, content, innerSpacingAfter) {
  92. var item = {
  93. type: "url"
  94. };
  95. if(innerSpacingBefore) {
  96. item.innerSpacingBefore = innerSpacingBefore;
  97. }
  98. if(innerSpacingAfter) {
  99. item.innerSpacingAfter = innerSpacingAfter;
  100. }
  101. switch(content[0]) {
  102. case "\"":
  103. item.stringType = "\"";
  104. item.url = unescapeString(content.substr(1, content.length - 2));
  105. break;
  106. case "'":
  107. item.stringType = "'";
  108. item.url = unescapeString(content.substr(1, content.length - 2));
  109. break;
  110. default:
  111. item.url = unescapeString(content);
  112. break;
  113. }
  114. this.value.nodes.push(item);
  115. }
  116. var parser = new Parser({
  117. decl: {
  118. "^\\s+": initialSpacingMatch,
  119. "/\\*([\\s\\S]*?)\\*/": commentMatch,
  120. "\"((?:[^\\\\\"]|\\\\.)*)\"": stringMatch,
  121. "'((?:[^\\\\']|\\\\.)*)'": stringMatch,
  122. "url\\((\\s*)(\"(?:[^\\\\\"]|\\\\.)*\")(\\s*)\\)": urlMatch,
  123. "url\\((\\s*)('(?:[^\\\\']|\\\\.)*')(\\s*)\\)": urlMatch,
  124. "url\\((\\s*)((?:[^\\\\)'\"]|\\\\.)*)(\\s*)\\)": urlMatch,
  125. "([\\w\-]+)\\((\\s*)": nestedItemMatch,
  126. "(\\s*)(\\))": nestedItemEndMatch,
  127. ",(\\s*)": commaMatch,
  128. "\\s+$": endSpacingMatch,
  129. "\\s+": spacingMatch,
  130. "[^\\s,\)]+": itemMatch
  131. }
  132. });
  133. function parseValues(str) {
  134. var valueNode = {
  135. type: "value",
  136. nodes: []
  137. };
  138. var rootNode = {
  139. type: "values",
  140. nodes: [
  141. valueNode
  142. ]
  143. };
  144. parser.parse("decl", str, {
  145. stack: [],
  146. root: rootNode,
  147. value: valueNode
  148. });
  149. return rootNode;
  150. }
  151. module.exports = parseValues;