inline-style-parser.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.InlineStyleParser = factory());
  5. })(this, (function () { 'use strict';
  6. // http://www.w3.org/TR/CSS21/grammar.html
  7. // https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027
  8. var COMMENT_REGEX = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g;
  9. var NEWLINE_REGEX = /\n/g;
  10. var WHITESPACE_REGEX = /^\s*/;
  11. // declaration
  12. var PROPERTY_REGEX = /^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/;
  13. var COLON_REGEX = /^:\s*/;
  14. var VALUE_REGEX = /^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/;
  15. var SEMICOLON_REGEX = /^[;\s]*/;
  16. // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill
  17. var TRIM_REGEX = /^\s+|\s+$/g;
  18. // strings
  19. var NEWLINE = '\n';
  20. var FORWARD_SLASH = '/';
  21. var ASTERISK = '*';
  22. var EMPTY_STRING = '';
  23. // types
  24. var TYPE_COMMENT = 'comment';
  25. var TYPE_DECLARATION = 'declaration';
  26. /**
  27. * @param {String} style
  28. * @param {Object} [options]
  29. * @return {Object[]}
  30. * @throws {TypeError}
  31. * @throws {Error}
  32. */
  33. function index (style, options) {
  34. if (typeof style !== 'string') {
  35. throw new TypeError('First argument must be a string');
  36. }
  37. if (!style) return [];
  38. options = options || {};
  39. /**
  40. * Positional.
  41. */
  42. var lineno = 1;
  43. var column = 1;
  44. /**
  45. * Update lineno and column based on `str`.
  46. *
  47. * @param {String} str
  48. */
  49. function updatePosition(str) {
  50. var lines = str.match(NEWLINE_REGEX);
  51. if (lines) lineno += lines.length;
  52. var i = str.lastIndexOf(NEWLINE);
  53. column = ~i ? str.length - i : column + str.length;
  54. }
  55. /**
  56. * Mark position and patch `node.position`.
  57. *
  58. * @return {Function}
  59. */
  60. function position() {
  61. var start = { line: lineno, column: column };
  62. return function (node) {
  63. node.position = new Position(start);
  64. whitespace();
  65. return node;
  66. };
  67. }
  68. /**
  69. * Store position information for a node.
  70. *
  71. * @constructor
  72. * @property {Object} start
  73. * @property {Object} end
  74. * @property {undefined|String} source
  75. */
  76. function Position(start) {
  77. this.start = start;
  78. this.end = { line: lineno, column: column };
  79. this.source = options.source;
  80. }
  81. /**
  82. * Non-enumerable source string.
  83. */
  84. Position.prototype.content = style;
  85. /**
  86. * Error `msg`.
  87. *
  88. * @param {String} msg
  89. * @throws {Error}
  90. */
  91. function error(msg) {
  92. var err = new Error(
  93. options.source + ':' + lineno + ':' + column + ': ' + msg
  94. );
  95. err.reason = msg;
  96. err.filename = options.source;
  97. err.line = lineno;
  98. err.column = column;
  99. err.source = style;
  100. if (options.silent) ; else {
  101. throw err;
  102. }
  103. }
  104. /**
  105. * Match `re` and return captures.
  106. *
  107. * @param {RegExp} re
  108. * @return {undefined|Array}
  109. */
  110. function match(re) {
  111. var m = re.exec(style);
  112. if (!m) return;
  113. var str = m[0];
  114. updatePosition(str);
  115. style = style.slice(str.length);
  116. return m;
  117. }
  118. /**
  119. * Parse whitespace.
  120. */
  121. function whitespace() {
  122. match(WHITESPACE_REGEX);
  123. }
  124. /**
  125. * Parse comments.
  126. *
  127. * @param {Object[]} [rules]
  128. * @return {Object[]}
  129. */
  130. function comments(rules) {
  131. var c;
  132. rules = rules || [];
  133. while ((c = comment())) {
  134. if (c !== false) {
  135. rules.push(c);
  136. }
  137. }
  138. return rules;
  139. }
  140. /**
  141. * Parse comment.
  142. *
  143. * @return {Object}
  144. * @throws {Error}
  145. */
  146. function comment() {
  147. var pos = position();
  148. if (FORWARD_SLASH != style.charAt(0) || ASTERISK != style.charAt(1)) return;
  149. var i = 2;
  150. while (
  151. EMPTY_STRING != style.charAt(i) &&
  152. (ASTERISK != style.charAt(i) || FORWARD_SLASH != style.charAt(i + 1))
  153. ) {
  154. ++i;
  155. }
  156. i += 2;
  157. if (EMPTY_STRING === style.charAt(i - 1)) {
  158. return error('End of comment missing');
  159. }
  160. var str = style.slice(2, i - 2);
  161. column += 2;
  162. updatePosition(str);
  163. style = style.slice(i);
  164. column += 2;
  165. return pos({
  166. type: TYPE_COMMENT,
  167. comment: str
  168. });
  169. }
  170. /**
  171. * Parse declaration.
  172. *
  173. * @return {Object}
  174. * @throws {Error}
  175. */
  176. function declaration() {
  177. var pos = position();
  178. // prop
  179. var prop = match(PROPERTY_REGEX);
  180. if (!prop) return;
  181. comment();
  182. // :
  183. if (!match(COLON_REGEX)) return error("property missing ':'");
  184. // val
  185. var val = match(VALUE_REGEX);
  186. var ret = pos({
  187. type: TYPE_DECLARATION,
  188. property: trim(prop[0].replace(COMMENT_REGEX, EMPTY_STRING)),
  189. value: val
  190. ? trim(val[0].replace(COMMENT_REGEX, EMPTY_STRING))
  191. : EMPTY_STRING
  192. });
  193. // ;
  194. match(SEMICOLON_REGEX);
  195. return ret;
  196. }
  197. /**
  198. * Parse declarations.
  199. *
  200. * @return {Object[]}
  201. */
  202. function declarations() {
  203. var decls = [];
  204. comments(decls);
  205. // declarations
  206. var decl;
  207. while ((decl = declaration())) {
  208. if (decl !== false) {
  209. decls.push(decl);
  210. comments(decls);
  211. }
  212. }
  213. return decls;
  214. }
  215. whitespace();
  216. return declarations();
  217. }
  218. /**
  219. * Trim `str`.
  220. *
  221. * @param {String} str
  222. * @return {String}
  223. */
  224. function trim(str) {
  225. return str ? str.replace(TRIM_REGEX, EMPTY_STRING) : EMPTY_STRING;
  226. }
  227. return index;
  228. }));
  229. //# sourceMappingURL=inline-style-parser.js.map