1"use strict"; 2 3exports.__esModule = true; 4exports["default"] = void 0; 5var _root = _interopRequireDefault(require("./selectors/root")); 6var _selector = _interopRequireDefault(require("./selectors/selector")); 7var _className = _interopRequireDefault(require("./selectors/className")); 8var _comment = _interopRequireDefault(require("./selectors/comment")); 9var _id = _interopRequireDefault(require("./selectors/id")); 10var _tag = _interopRequireDefault(require("./selectors/tag")); 11var _string = _interopRequireDefault(require("./selectors/string")); 12var _pseudo = _interopRequireDefault(require("./selectors/pseudo")); 13var _attribute = _interopRequireWildcard(require("./selectors/attribute")); 14var _universal = _interopRequireDefault(require("./selectors/universal")); 15var _combinator = _interopRequireDefault(require("./selectors/combinator")); 16var _nesting = _interopRequireDefault(require("./selectors/nesting")); 17var _sortAscending = _interopRequireDefault(require("./sortAscending")); 18var _tokenize = _interopRequireWildcard(require("./tokenize")); 19var tokens = _interopRequireWildcard(require("./tokenTypes")); 20var types = _interopRequireWildcard(require("./selectors/types")); 21var _util = require("./util"); 22var _WHITESPACE_TOKENS, _Object$assign; 23function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } 24function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 25function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 26function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 27function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } 28var WHITESPACE_TOKENS = (_WHITESPACE_TOKENS = {}, _WHITESPACE_TOKENS[tokens.space] = true, _WHITESPACE_TOKENS[tokens.cr] = true, _WHITESPACE_TOKENS[tokens.feed] = true, _WHITESPACE_TOKENS[tokens.newline] = true, _WHITESPACE_TOKENS[tokens.tab] = true, _WHITESPACE_TOKENS); 29var WHITESPACE_EQUIV_TOKENS = Object.assign({}, WHITESPACE_TOKENS, (_Object$assign = {}, _Object$assign[tokens.comment] = true, _Object$assign)); 30function tokenStart(token) { 31 return { 32 line: token[_tokenize.FIELDS.START_LINE], 33 column: token[_tokenize.FIELDS.START_COL] 34 }; 35} 36function tokenEnd(token) { 37 return { 38 line: token[_tokenize.FIELDS.END_LINE], 39 column: token[_tokenize.FIELDS.END_COL] 40 }; 41} 42function getSource(startLine, startColumn, endLine, endColumn) { 43 return { 44 start: { 45 line: startLine, 46 column: startColumn 47 }, 48 end: { 49 line: endLine, 50 column: endColumn 51 } 52 }; 53} 54function getTokenSource(token) { 55 return getSource(token[_tokenize.FIELDS.START_LINE], token[_tokenize.FIELDS.START_COL], token[_tokenize.FIELDS.END_LINE], token[_tokenize.FIELDS.END_COL]); 56} 57function getTokenSourceSpan(startToken, endToken) { 58 if (!startToken) { 59 return undefined; 60 } 61 return getSource(startToken[_tokenize.FIELDS.START_LINE], startToken[_tokenize.FIELDS.START_COL], endToken[_tokenize.FIELDS.END_LINE], endToken[_tokenize.FIELDS.END_COL]); 62} 63function unescapeProp(node, prop) { 64 var value = node[prop]; 65 if (typeof value !== "string") { 66 return; 67 } 68 if (value.indexOf("\\") !== -1) { 69 (0, _util.ensureObject)(node, 'raws'); 70 node[prop] = (0, _util.unesc)(value); 71 if (node.raws[prop] === undefined) { 72 node.raws[prop] = value; 73 } 74 } 75 return node; 76} 77function indexesOf(array, item) { 78 var i = -1; 79 var indexes = []; 80 while ((i = array.indexOf(item, i + 1)) !== -1) { 81 indexes.push(i); 82 } 83 return indexes; 84} 85function uniqs() { 86 var list = Array.prototype.concat.apply([], arguments); 87 return list.filter(function (item, i) { 88 return i === list.indexOf(item); 89 }); 90} 91var Parser = /*#__PURE__*/function () { 92 function Parser(rule, options) { 93 if (options === void 0) { 94 options = {}; 95 } 96 this.rule = rule; 97 this.options = Object.assign({ 98 lossy: false, 99 safe: false 100 }, options); 101 this.position = 0; 102 this.css = typeof this.rule === 'string' ? this.rule : this.rule.selector; 103 this.tokens = (0, _tokenize["default"])({ 104 css: this.css, 105 error: this._errorGenerator(), 106 safe: this.options.safe 107 }); 108 var rootSource = getTokenSourceSpan(this.tokens[0], this.tokens[this.tokens.length - 1]); 109 this.root = new _root["default"]({ 110 source: rootSource 111 }); 112 this.root.errorGenerator = this._errorGenerator(); 113 var selector = new _selector["default"]({ 114 source: { 115 start: { 116 line: 1, 117 column: 1 118 } 119 } 120 }); 121 this.root.append(selector); 122 this.current = selector; 123 this.loop(); 124 } 125 var _proto = Parser.prototype; 126 _proto._errorGenerator = function _errorGenerator() { 127 var _this = this; 128 return function (message, errorOptions) { 129 if (typeof _this.rule === 'string') { 130 return new Error(message); 131 } 132 return _this.rule.error(message, errorOptions); 133 }; 134 }; 135 _proto.attribute = function attribute() { 136 var attr = []; 137 var startingToken = this.currToken; 138 this.position++; 139 while (this.position < this.tokens.length && this.currToken[_tokenize.FIELDS.TYPE] !== tokens.closeSquare) { 140 attr.push(this.currToken); 141 this.position++; 142 } 143 if (this.currToken[_tokenize.FIELDS.TYPE] !== tokens.closeSquare) { 144 return this.expected('closing square bracket', this.currToken[_tokenize.FIELDS.START_POS]); 145 } 146 var len = attr.length; 147 var node = { 148 source: getSource(startingToken[1], startingToken[2], this.currToken[3], this.currToken[4]), 149 sourceIndex: startingToken[_tokenize.FIELDS.START_POS] 150 }; 151 if (len === 1 && !~[tokens.word].indexOf(attr[0][_tokenize.FIELDS.TYPE])) { 152 return this.expected('attribute', attr[0][_tokenize.FIELDS.START_POS]); 153 } 154 var pos = 0; 155 var spaceBefore = ''; 156 var commentBefore = ''; 157 var lastAdded = null; 158 var spaceAfterMeaningfulToken = false; 159 while (pos < len) { 160 var token = attr[pos]; 161 var content = this.content(token); 162 var next = attr[pos + 1]; 163 switch (token[_tokenize.FIELDS.TYPE]) { 164 case tokens.space: 165 // if ( 166 // len === 1 || 167 // pos === 0 && this.content(next) === '|' 168 // ) { 169 // return this.expected('attribute', token[TOKEN.START_POS], content); 170 // } 171 spaceAfterMeaningfulToken = true; 172 if (this.options.lossy) { 173 break; 174 } 175 if (lastAdded) { 176 (0, _util.ensureObject)(node, 'spaces', lastAdded); 177 var prevContent = node.spaces[lastAdded].after || ''; 178 node.spaces[lastAdded].after = prevContent + content; 179 var existingComment = (0, _util.getProp)(node, 'raws', 'spaces', lastAdded, 'after') || null; 180 if (existingComment) { 181 node.raws.spaces[lastAdded].after = existingComment + content; 182 } 183 } else { 184 spaceBefore = spaceBefore + content; 185 commentBefore = commentBefore + content; 186 } 187 break; 188 case tokens.asterisk: 189 if (next[_tokenize.FIELDS.TYPE] === tokens.equals) { 190 node.operator = content; 191 lastAdded = 'operator'; 192 } else if ((!node.namespace || lastAdded === "namespace" && !spaceAfterMeaningfulToken) && next) { 193 if (spaceBefore) { 194 (0, _util.ensureObject)(node, 'spaces', 'attribute'); 195 node.spaces.attribute.before = spaceBefore; 196 spaceBefore = ''; 197 } 198 if (commentBefore) { 199 (0, _util.ensureObject)(node, 'raws', 'spaces', 'attribute'); 200 node.raws.spaces.attribute.before = spaceBefore; 201 commentBefore = ''; 202 } 203 node.namespace = (node.namespace || "") + content; 204 var rawValue = (0, _util.getProp)(node, 'raws', 'namespace') || null; 205 if (rawValue) { 206 node.raws.namespace += content; 207 } 208 lastAdded = 'namespace'; 209 } 210 spaceAfterMeaningfulToken = false; 211 break; 212 case tokens.dollar: 213 if (lastAdded === "value") { 214 var oldRawValue = (0, _util.getProp)(node, 'raws', 'value'); 215 node.value += "$"; 216 if (oldRawValue) { 217 node.raws.value = oldRawValue + "$"; 218 } 219 break; 220 } 221 // Falls through 222 case tokens.caret: 223 if (next[_tokenize.FIELDS.TYPE] === tokens.equals) { 224 node.operator = content; 225 lastAdded = 'operator'; 226 } 227 spaceAfterMeaningfulToken = false; 228 break; 229 case tokens.combinator: 230 if (content === '~' && next[_tokenize.FIELDS.TYPE] === tokens.equals) { 231 node.operator = content; 232 lastAdded = 'operator'; 233 } 234 if (content !== '|') { 235 spaceAfterMeaningfulToken = false; 236 break; 237 } 238 if (next[_tokenize.FIELDS.TYPE] === tokens.equals) { 239 node.operator = content; 240 lastAdded = 'operator'; 241 } else if (!node.namespace && !node.attribute) { 242 node.namespace = true; 243 } 244 spaceAfterMeaningfulToken = false; 245 break; 246 case tokens.word: 247 if (next && this.content(next) === '|' && attr[pos + 2] && attr[pos + 2][_tokenize.FIELDS.TYPE] !== tokens.equals && 248 // this look-ahead probably fails with comment nodes involved. 249 !node.operator && !node.namespace) { 250 node.namespace = content; 251 lastAdded = 'namespace'; 252 } else if (!node.attribute || lastAdded === "attribute" && !spaceAfterMeaningfulToken) { 253 if (spaceBefore) { 254 (0, _util.ensureObject)(node, 'spaces', 'attribute'); 255 node.spaces.attribute.before = spaceBefore; 256 spaceBefore = ''; 257 } 258 if (commentBefore) { 259 (0, _util.ensureObject)(node, 'raws', 'spaces', 'attribute'); 260 node.raws.spaces.attribute.before = commentBefore; 261 commentBefore = ''; 262 } 263 node.attribute = (node.attribute || "") + content; 264 var _rawValue = (0, _util.getProp)(node, 'raws', 'attribute') || null; 265 if (_rawValue) { 266 node.raws.attribute += content; 267 } 268 lastAdded = 'attribute'; 269 } else if (!node.value && node.value !== "" || lastAdded === "value" && !(spaceAfterMeaningfulToken || node.quoteMark)) { 270 var _unescaped = (0, _util.unesc)(content); 271 var _oldRawValue = (0, _util.getProp)(node, 'raws', 'value') || ''; 272 var oldValue = node.value || ''; 273 node.value = oldValue + _unescaped; 274 node.quoteMark = null; 275 if (_unescaped !== content || _oldRawValue) { 276 (0, _util.ensureObject)(node, 'raws'); 277 node.raws.value = (_oldRawValue || oldValue) + content; 278 } 279 lastAdded = 'value'; 280 } else { 281 var insensitive = content === 'i' || content === "I"; 282 if ((node.value || node.value === '') && (node.quoteMark || spaceAfterMeaningfulToken)) { 283 node.insensitive = insensitive; 284 if (!insensitive || content === "I") { 285 (0, _util.ensureObject)(node, 'raws'); 286 node.raws.insensitiveFlag = content; 287 } 288 lastAdded = 'insensitive'; 289 if (spaceBefore) { 290 (0, _util.ensureObject)(node, 'spaces', 'insensitive'); 291 node.spaces.insensitive.before = spaceBefore; 292 spaceBefore = ''; 293 } 294 if (commentBefore) { 295 (0, _util.ensureObject)(node, 'raws', 'spaces', 'insensitive'); 296 node.raws.spaces.insensitive.before = commentBefore; 297 commentBefore = ''; 298 } 299 } else if (node.value || node.value === '') { 300 lastAdded = 'value'; 301 node.value += content; 302 if (node.raws.value) { 303 node.raws.value += content; 304 } 305 } 306 } 307 spaceAfterMeaningfulToken = false; 308 break; 309 case tokens.str: 310 if (!node.attribute || !node.operator) { 311 return this.error("Expected an attribute followed by an operator preceding the string.", { 312 index: token[_tokenize.FIELDS.START_POS] 313 }); 314 } 315 var _unescapeValue = (0, _attribute.unescapeValue)(content), 316 unescaped = _unescapeValue.unescaped, 317 quoteMark = _unescapeValue.quoteMark; 318 node.value = unescaped; 319 node.quoteMark = quoteMark; 320 lastAdded = 'value'; 321 (0, _util.ensureObject)(node, 'raws'); 322 node.raws.value = content; 323 spaceAfterMeaningfulToken = false; 324 break; 325 case tokens.equals: 326 if (!node.attribute) { 327 return this.expected('attribute', token[_tokenize.FIELDS.START_POS], content); 328 } 329 if (node.value) { 330 return this.error('Unexpected "=" found; an operator was already defined.', { 331 index: token[_tokenize.FIELDS.START_POS] 332 }); 333 } 334 node.operator = node.operator ? node.operator + content : content; 335 lastAdded = 'operator'; 336 spaceAfterMeaningfulToken = false; 337 break; 338 case tokens.comment: 339 if (lastAdded) { 340 if (spaceAfterMeaningfulToken || next && next[_tokenize.FIELDS.TYPE] === tokens.space || lastAdded === 'insensitive') { 341 var lastComment = (0, _util.getProp)(node, 'spaces', lastAdded, 'after') || ''; 342 var rawLastComment = (0, _util.getProp)(node, 'raws', 'spaces', lastAdded, 'after') || lastComment; 343 (0, _util.ensureObject)(node, 'raws', 'spaces', lastAdded); 344 node.raws.spaces[lastAdded].after = rawLastComment + content; 345 } else { 346 var lastValue = node[lastAdded] || ''; 347 var rawLastValue = (0, _util.getProp)(node, 'raws', lastAdded) || lastValue; 348 (0, _util.ensureObject)(node, 'raws'); 349 node.raws[lastAdded] = rawLastValue + content; 350 } 351 } else { 352 commentBefore = commentBefore + content; 353 } 354 break; 355 default: 356 return this.error("Unexpected \"" + content + "\" found.", { 357 index: token[_tokenize.FIELDS.START_POS] 358 }); 359 } 360 pos++; 361 } 362 unescapeProp(node, "attribute"); 363 unescapeProp(node, "namespace"); 364 this.newNode(new _attribute["default"](node)); 365 this.position++; 366 } 367 368 /** 369 * return a node containing meaningless garbage up to (but not including) the specified token position. 370 * if the token position is negative, all remaining tokens are consumed. 371 * 372 * This returns an array containing a single string node if all whitespace, 373 * otherwise an array of comment nodes with space before and after. 374 * 375 * These tokens are not added to the current selector, the caller can add them or use them to amend 376 * a previous node's space metadata. 377 * 378 * In lossy mode, this returns only comments. 379 */; 380 _proto.parseWhitespaceEquivalentTokens = function parseWhitespaceEquivalentTokens(stopPosition) { 381 if (stopPosition < 0) { 382 stopPosition = this.tokens.length; 383 } 384 var startPosition = this.position; 385 var nodes = []; 386 var space = ""; 387 var lastComment = undefined; 388 do { 389 if (WHITESPACE_TOKENS[this.currToken[_tokenize.FIELDS.TYPE]]) { 390 if (!this.options.lossy) { 391 space += this.content(); 392 } 393 } else if (this.currToken[_tokenize.FIELDS.TYPE] === tokens.comment) { 394 var spaces = {}; 395 if (space) { 396 spaces.before = space; 397 space = ""; 398 } 399 lastComment = new _comment["default"]({ 400 value: this.content(), 401 source: getTokenSource(this.currToken), 402 sourceIndex: this.currToken[_tokenize.FIELDS.START_POS], 403 spaces: spaces 404 }); 405 nodes.push(lastComment); 406 } 407 } while (++this.position < stopPosition); 408 if (space) { 409 if (lastComment) { 410 lastComment.spaces.after = space; 411 } else if (!this.options.lossy) { 412 var firstToken = this.tokens[startPosition]; 413 var lastToken = this.tokens[this.position - 1]; 414 nodes.push(new _string["default"]({ 415 value: '', 416 source: getSource(firstToken[_tokenize.FIELDS.START_LINE], firstToken[_tokenize.FIELDS.START_COL], lastToken[_tokenize.FIELDS.END_LINE], lastToken[_tokenize.FIELDS.END_COL]), 417 sourceIndex: firstToken[_tokenize.FIELDS.START_POS], 418 spaces: { 419 before: space, 420 after: '' 421 } 422 })); 423 } 424 } 425 return nodes; 426 } 427 428 /** 429 * 430 * @param {*} nodes 431 */; 432 _proto.convertWhitespaceNodesToSpace = function convertWhitespaceNodesToSpace(nodes, requiredSpace) { 433 var _this2 = this; 434 if (requiredSpace === void 0) { 435 requiredSpace = false; 436 } 437 var space = ""; 438 var rawSpace = ""; 439 nodes.forEach(function (n) { 440 var spaceBefore = _this2.lossySpace(n.spaces.before, requiredSpace); 441 var rawSpaceBefore = _this2.lossySpace(n.rawSpaceBefore, requiredSpace); 442 space += spaceBefore + _this2.lossySpace(n.spaces.after, requiredSpace && spaceBefore.length === 0); 443 rawSpace += spaceBefore + n.value + _this2.lossySpace(n.rawSpaceAfter, requiredSpace && rawSpaceBefore.length === 0); 444 }); 445 if (rawSpace === space) { 446 rawSpace = undefined; 447 } 448 var result = { 449 space: space, 450 rawSpace: rawSpace 451 }; 452 return result; 453 }; 454 _proto.isNamedCombinator = function isNamedCombinator(position) { 455 if (position === void 0) { 456 position = this.position; 457 } 458 return this.tokens[position + 0] && this.tokens[position + 0][_tokenize.FIELDS.TYPE] === tokens.slash && this.tokens[position + 1] && this.tokens[position + 1][_tokenize.FIELDS.TYPE] === tokens.word && this.tokens[position + 2] && this.tokens[position + 2][_tokenize.FIELDS.TYPE] === tokens.slash; 459 }; 460 _proto.namedCombinator = function namedCombinator() { 461 if (this.isNamedCombinator()) { 462 var nameRaw = this.content(this.tokens[this.position + 1]); 463 var name = (0, _util.unesc)(nameRaw).toLowerCase(); 464 var raws = {}; 465 if (name !== nameRaw) { 466 raws.value = "/" + nameRaw + "/"; 467 } 468 var node = new _combinator["default"]({ 469 value: "/" + name + "/", 470 source: getSource(this.currToken[_tokenize.FIELDS.START_LINE], this.currToken[_tokenize.FIELDS.START_COL], this.tokens[this.position + 2][_tokenize.FIELDS.END_LINE], this.tokens[this.position + 2][_tokenize.FIELDS.END_COL]), 471 sourceIndex: this.currToken[_tokenize.FIELDS.START_POS], 472 raws: raws 473 }); 474 this.position = this.position + 3; 475 return node; 476 } else { 477 this.unexpected(); 478 } 479 }; 480 _proto.combinator = function combinator() { 481 var _this3 = this; 482 if (this.content() === '|') { 483 return this.namespace(); 484 } 485 // We need to decide between a space that's a descendant combinator and meaningless whitespace at the end of a selector. 486 var nextSigTokenPos = this.locateNextMeaningfulToken(this.position); 487 if (nextSigTokenPos < 0 || this.tokens[nextSigTokenPos][_tokenize.FIELDS.TYPE] === tokens.comma) { 488 var nodes = this.parseWhitespaceEquivalentTokens(nextSigTokenPos); 489 if (nodes.length > 0) { 490 var last = this.current.last; 491 if (last) { 492 var _this$convertWhitespa = this.convertWhitespaceNodesToSpace(nodes), 493 space = _this$convertWhitespa.space, 494 rawSpace = _this$convertWhitespa.rawSpace; 495 if (rawSpace !== undefined) { 496 last.rawSpaceAfter += rawSpace; 497 } 498 last.spaces.after += space; 499 } else { 500 nodes.forEach(function (n) { 501 return _this3.newNode(n); 502 }); 503 } 504 } 505 return; 506 } 507 var firstToken = this.currToken; 508 var spaceOrDescendantSelectorNodes = undefined; 509 if (nextSigTokenPos > this.position) { 510 spaceOrDescendantSelectorNodes = this.parseWhitespaceEquivalentTokens(nextSigTokenPos); 511 } 512 var node; 513 if (this.isNamedCombinator()) { 514 node = this.namedCombinator(); 515 } else if (this.currToken[_tokenize.FIELDS.TYPE] === tokens.combinator) { 516 node = new _combinator["default"]({ 517 value: this.content(), 518 source: getTokenSource(this.currToken), 519 sourceIndex: this.currToken[_tokenize.FIELDS.START_POS] 520 }); 521 this.position++; 522 } else if (WHITESPACE_TOKENS[this.currToken[_tokenize.FIELDS.TYPE]]) { 523 // pass 524 } else if (!spaceOrDescendantSelectorNodes) { 525 this.unexpected(); 526 } 527 if (node) { 528 if (spaceOrDescendantSelectorNodes) { 529 var _this$convertWhitespa2 = this.convertWhitespaceNodesToSpace(spaceOrDescendantSelectorNodes), 530 _space = _this$convertWhitespa2.space, 531 _rawSpace = _this$convertWhitespa2.rawSpace; 532 node.spaces.before = _space; 533 node.rawSpaceBefore = _rawSpace; 534 } 535 } else { 536 // descendant combinator 537 var _this$convertWhitespa3 = this.convertWhitespaceNodesToSpace(spaceOrDescendantSelectorNodes, true), 538 _space2 = _this$convertWhitespa3.space, 539 _rawSpace2 = _this$convertWhitespa3.rawSpace; 540 if (!_rawSpace2) { 541 _rawSpace2 = _space2; 542 } 543 var spaces = {}; 544 var raws = { 545 spaces: {} 546 }; 547 if (_space2.endsWith(' ') && _rawSpace2.endsWith(' ')) { 548 spaces.before = _space2.slice(0, _space2.length - 1); 549 raws.spaces.before = _rawSpace2.slice(0, _rawSpace2.length - 1); 550 } else if (_space2.startsWith(' ') && _rawSpace2.startsWith(' ')) { 551 spaces.after = _space2.slice(1); 552 raws.spaces.after = _rawSpace2.slice(1); 553 } else { 554 raws.value = _rawSpace2; 555 } 556 node = new _combinator["default"]({ 557 value: ' ', 558 source: getTokenSourceSpan(firstToken, this.tokens[this.position - 1]), 559 sourceIndex: firstToken[_tokenize.FIELDS.START_POS], 560 spaces: spaces, 561 raws: raws 562 }); 563 } 564 if (this.currToken && this.currToken[_tokenize.FIELDS.TYPE] === tokens.space) { 565 node.spaces.after = this.optionalSpace(this.content()); 566 this.position++; 567 } 568 return this.newNode(node); 569 }; 570 _proto.comma = function comma() { 571 if (this.position === this.tokens.length - 1) { 572 this.root.trailingComma = true; 573 this.position++; 574 return; 575 } 576 this.current._inferEndPosition(); 577 var selector = new _selector["default"]({ 578 source: { 579 start: tokenStart(this.tokens[this.position + 1]) 580 } 581 }); 582 this.current.parent.append(selector); 583 this.current = selector; 584 this.position++; 585 }; 586 _proto.comment = function comment() { 587 var current = this.currToken; 588 this.newNode(new _comment["default"]({ 589 value: this.content(), 590 source: getTokenSource(current), 591 sourceIndex: current[_tokenize.FIELDS.START_POS] 592 })); 593 this.position++; 594 }; 595 _proto.error = function error(message, opts) { 596 throw this.root.error(message, opts); 597 }; 598 _proto.missingBackslash = function missingBackslash() { 599 return this.error('Expected a backslash preceding the semicolon.', { 600 index: this.currToken[_tokenize.FIELDS.START_POS] 601 }); 602 }; 603 _proto.missingParenthesis = function missingParenthesis() { 604 return this.expected('opening parenthesis', this.currToken[_tokenize.FIELDS.START_POS]); 605 }; 606 _proto.missingSquareBracket = function missingSquareBracket() { 607 return this.expected('opening square bracket', this.currToken[_tokenize.FIELDS.START_POS]); 608 }; 609 _proto.unexpected = function unexpected() { 610 return this.error("Unexpected '" + this.content() + "'. Escaping special characters with \\ may help.", this.currToken[_tokenize.FIELDS.START_POS]); 611 }; 612 _proto.unexpectedPipe = function unexpectedPipe() { 613 return this.error("Unexpected '|'.", this.currToken[_tokenize.FIELDS.START_POS]); 614 }; 615 _proto.namespace = function namespace() { 616 var before = this.prevToken && this.content(this.prevToken) || true; 617 if (this.nextToken[_tokenize.FIELDS.TYPE] === tokens.word) { 618 this.position++; 619 return this.word(before); 620 } else if (this.nextToken[_tokenize.FIELDS.TYPE] === tokens.asterisk) { 621 this.position++; 622 return this.universal(before); 623 } 624 this.unexpectedPipe(); 625 }; 626 _proto.nesting = function nesting() { 627 if (this.nextToken) { 628 var nextContent = this.content(this.nextToken); 629 if (nextContent === "|") { 630 this.position++; 631 return; 632 } 633 } 634 var current = this.currToken; 635 this.newNode(new _nesting["default"]({ 636 value: this.content(), 637 source: getTokenSource(current), 638 sourceIndex: current[_tokenize.FIELDS.START_POS] 639 })); 640 this.position++; 641 }; 642 _proto.parentheses = function parentheses() { 643 var last = this.current.last; 644 var unbalanced = 1; 645 this.position++; 646 if (last && last.type === types.PSEUDO) { 647 var selector = new _selector["default"]({ 648 source: { 649 start: tokenStart(this.tokens[this.position - 1]) 650 } 651 }); 652 var cache = this.current; 653 last.append(selector); 654 this.current = selector; 655 while (this.position < this.tokens.length && unbalanced) { 656 if (this.currToken[_tokenize.FIELDS.TYPE] === tokens.openParenthesis) { 657 unbalanced++; 658 } 659 if (this.currToken[_tokenize.FIELDS.TYPE] === tokens.closeParenthesis) { 660 unbalanced--; 661 } 662 if (unbalanced) { 663 this.parse(); 664 } else { 665 this.current.source.end = tokenEnd(this.currToken); 666 this.current.parent.source.end = tokenEnd(this.currToken); 667 this.position++; 668 } 669 } 670 this.current = cache; 671 } else { 672 // I think this case should be an error. It's used to implement a basic parse of media queries 673 // but I don't think it's a good idea. 674 var parenStart = this.currToken; 675 var parenValue = "("; 676 var parenEnd; 677 while (this.position < this.tokens.length && unbalanced) { 678 if (this.currToken[_tokenize.FIELDS.TYPE] === tokens.openParenthesis) { 679 unbalanced++; 680 } 681 if (this.currToken[_tokenize.FIELDS.TYPE] === tokens.closeParenthesis) { 682 unbalanced--; 683 } 684 parenEnd = this.currToken; 685 parenValue += this.parseParenthesisToken(this.currToken); 686 this.position++; 687 } 688 if (last) { 689 last.appendToPropertyAndEscape("value", parenValue, parenValue); 690 } else { 691 this.newNode(new _string["default"]({ 692 value: parenValue, 693 source: getSource(parenStart[_tokenize.FIELDS.START_LINE], parenStart[_tokenize.FIELDS.START_COL], parenEnd[_tokenize.FIELDS.END_LINE], parenEnd[_tokenize.FIELDS.END_COL]), 694 sourceIndex: parenStart[_tokenize.FIELDS.START_POS] 695 })); 696 } 697 } 698 if (unbalanced) { 699 return this.expected('closing parenthesis', this.currToken[_tokenize.FIELDS.START_POS]); 700 } 701 }; 702 _proto.pseudo = function pseudo() { 703 var _this4 = this; 704 var pseudoStr = ''; 705 var startingToken = this.currToken; 706 while (this.currToken && this.currToken[_tokenize.FIELDS.TYPE] === tokens.colon) { 707 pseudoStr += this.content(); 708 this.position++; 709 } 710 if (!this.currToken) { 711 return this.expected(['pseudo-class', 'pseudo-element'], this.position - 1); 712 } 713 if (this.currToken[_tokenize.FIELDS.TYPE] === tokens.word) { 714 this.splitWord(false, function (first, length) { 715 pseudoStr += first; 716 _this4.newNode(new _pseudo["default"]({ 717 value: pseudoStr, 718 source: getTokenSourceSpan(startingToken, _this4.currToken), 719 sourceIndex: startingToken[_tokenize.FIELDS.START_POS] 720 })); 721 if (length > 1 && _this4.nextToken && _this4.nextToken[_tokenize.FIELDS.TYPE] === tokens.openParenthesis) { 722 _this4.error('Misplaced parenthesis.', { 723 index: _this4.nextToken[_tokenize.FIELDS.START_POS] 724 }); 725 } 726 }); 727 } else { 728 return this.expected(['pseudo-class', 'pseudo-element'], this.currToken[_tokenize.FIELDS.START_POS]); 729 } 730 }; 731 _proto.space = function space() { 732 var content = this.content(); 733 // Handle space before and after the selector 734 if (this.position === 0 || this.prevToken[_tokenize.FIELDS.TYPE] === tokens.comma || this.prevToken[_tokenize.FIELDS.TYPE] === tokens.openParenthesis || this.current.nodes.every(function (node) { 735 return node.type === 'comment'; 736 })) { 737 this.spaces = this.optionalSpace(content); 738 this.position++; 739 } else if (this.position === this.tokens.length - 1 || this.nextToken[_tokenize.FIELDS.TYPE] === tokens.comma || this.nextToken[_tokenize.FIELDS.TYPE] === tokens.closeParenthesis) { 740 this.current.last.spaces.after = this.optionalSpace(content); 741 this.position++; 742 } else { 743 this.combinator(); 744 } 745 }; 746 _proto.string = function string() { 747 var current = this.currToken; 748 this.newNode(new _string["default"]({ 749 value: this.content(), 750 source: getTokenSource(current), 751 sourceIndex: current[_tokenize.FIELDS.START_POS] 752 })); 753 this.position++; 754 }; 755 _proto.universal = function universal(namespace) { 756 var nextToken = this.nextToken; 757 if (nextToken && this.content(nextToken) === '|') { 758 this.position++; 759 return this.namespace(); 760 } 761 var current = this.currToken; 762 this.newNode(new _universal["default"]({ 763 value: this.content(), 764 source: getTokenSource(current), 765 sourceIndex: current[_tokenize.FIELDS.START_POS] 766 }), namespace); 767 this.position++; 768 }; 769 _proto.splitWord = function splitWord(namespace, firstCallback) { 770 var _this5 = this; 771 var nextToken = this.nextToken; 772 var word = this.content(); 773 while (nextToken && ~[tokens.dollar, tokens.caret, tokens.equals, tokens.word].indexOf(nextToken[_tokenize.FIELDS.TYPE])) { 774 this.position++; 775 var current = this.content(); 776 word += current; 777 if (current.lastIndexOf('\\') === current.length - 1) { 778 var next = this.nextToken; 779 if (next && next[_tokenize.FIELDS.TYPE] === tokens.space) { 780 word += this.requiredSpace(this.content(next)); 781 this.position++; 782 } 783 } 784 nextToken = this.nextToken; 785 } 786 var hasClass = indexesOf(word, '.').filter(function (i) { 787 // Allow escaped dot within class name 788 var escapedDot = word[i - 1] === '\\'; 789 // Allow decimal numbers percent in @keyframes 790 var isKeyframesPercent = /^\d+\.\d+%$/.test(word); 791 return !escapedDot && !isKeyframesPercent; 792 }); 793 var hasId = indexesOf(word, '#').filter(function (i) { 794 return word[i - 1] !== '\\'; 795 }); 796 // Eliminate Sass interpolations from the list of id indexes 797 var interpolations = indexesOf(word, '#{'); 798 if (interpolations.length) { 799 hasId = hasId.filter(function (hashIndex) { 800 return !~interpolations.indexOf(hashIndex); 801 }); 802 } 803 var indices = (0, _sortAscending["default"])(uniqs([0].concat(hasClass, hasId))); 804 indices.forEach(function (ind, i) { 805 var index = indices[i + 1] || word.length; 806 var value = word.slice(ind, index); 807 if (i === 0 && firstCallback) { 808 return firstCallback.call(_this5, value, indices.length); 809 } 810 var node; 811 var current = _this5.currToken; 812 var sourceIndex = current[_tokenize.FIELDS.START_POS] + indices[i]; 813 var source = getSource(current[1], current[2] + ind, current[3], current[2] + (index - 1)); 814 if (~hasClass.indexOf(ind)) { 815 var classNameOpts = { 816 value: value.slice(1), 817 source: source, 818 sourceIndex: sourceIndex 819 }; 820 node = new _className["default"](unescapeProp(classNameOpts, "value")); 821 } else if (~hasId.indexOf(ind)) { 822 var idOpts = { 823 value: value.slice(1), 824 source: source, 825 sourceIndex: sourceIndex 826 }; 827 node = new _id["default"](unescapeProp(idOpts, "value")); 828 } else { 829 var tagOpts = { 830 value: value, 831 source: source, 832 sourceIndex: sourceIndex 833 }; 834 unescapeProp(tagOpts, "value"); 835 node = new _tag["default"](tagOpts); 836 } 837 _this5.newNode(node, namespace); 838 // Ensure that the namespace is used only once 839 namespace = null; 840 }); 841 this.position++; 842 }; 843 _proto.word = function word(namespace) { 844 var nextToken = this.nextToken; 845 if (nextToken && this.content(nextToken) === '|') { 846 this.position++; 847 return this.namespace(); 848 } 849 return this.splitWord(namespace); 850 }; 851 _proto.loop = function loop() { 852 while (this.position < this.tokens.length) { 853 this.parse(true); 854 } 855 this.current._inferEndPosition(); 856 return this.root; 857 }; 858 _proto.parse = function parse(throwOnParenthesis) { 859 switch (this.currToken[_tokenize.FIELDS.TYPE]) { 860 case tokens.space: 861 this.space(); 862 break; 863 case tokens.comment: 864 this.comment(); 865 break; 866 case tokens.openParenthesis: 867 this.parentheses(); 868 break; 869 case tokens.closeParenthesis: 870 if (throwOnParenthesis) { 871 this.missingParenthesis(); 872 } 873 break; 874 case tokens.openSquare: 875 this.attribute(); 876 break; 877 case tokens.dollar: 878 case tokens.caret: 879 case tokens.equals: 880 case tokens.word: 881 this.word(); 882 break; 883 case tokens.colon: 884 this.pseudo(); 885 break; 886 case tokens.comma: 887 this.comma(); 888 break; 889 case tokens.asterisk: 890 this.universal(); 891 break; 892 case tokens.ampersand: 893 this.nesting(); 894 break; 895 case tokens.slash: 896 case tokens.combinator: 897 this.combinator(); 898 break; 899 case tokens.str: 900 this.string(); 901 break; 902 // These cases throw; no break needed. 903 case tokens.closeSquare: 904 this.missingSquareBracket(); 905 case tokens.semicolon: 906 this.missingBackslash(); 907 default: 908 this.unexpected(); 909 } 910 } 911 912 /** 913 * Helpers 914 */; 915 _proto.expected = function expected(description, index, found) { 916 if (Array.isArray(description)) { 917 var last = description.pop(); 918 description = description.join(', ') + " or " + last; 919 } 920 var an = /^[aeiou]/.test(description[0]) ? 'an' : 'a'; 921 if (!found) { 922 return this.error("Expected " + an + " " + description + ".", { 923 index: index 924 }); 925 } 926 return this.error("Expected " + an + " " + description + ", found \"" + found + "\" instead.", { 927 index: index 928 }); 929 }; 930 _proto.requiredSpace = function requiredSpace(space) { 931 return this.options.lossy ? ' ' : space; 932 }; 933 _proto.optionalSpace = function optionalSpace(space) { 934 return this.options.lossy ? '' : space; 935 }; 936 _proto.lossySpace = function lossySpace(space, required) { 937 if (this.options.lossy) { 938 return required ? ' ' : ''; 939 } else { 940 return space; 941 } 942 }; 943 _proto.parseParenthesisToken = function parseParenthesisToken(token) { 944 var content = this.content(token); 945 if (token[_tokenize.FIELDS.TYPE] === tokens.space) { 946 return this.requiredSpace(content); 947 } else { 948 return content; 949 } 950 }; 951 _proto.newNode = function newNode(node, namespace) { 952 if (namespace) { 953 if (/^ +$/.test(namespace)) { 954 if (!this.options.lossy) { 955 this.spaces = (this.spaces || '') + namespace; 956 } 957 namespace = true; 958 } 959 node.namespace = namespace; 960 unescapeProp(node, "namespace"); 961 } 962 if (this.spaces) { 963 node.spaces.before = this.spaces; 964 this.spaces = ''; 965 } 966 return this.current.append(node); 967 }; 968 _proto.content = function content(token) { 969 if (token === void 0) { 970 token = this.currToken; 971 } 972 return this.css.slice(token[_tokenize.FIELDS.START_POS], token[_tokenize.FIELDS.END_POS]); 973 }; 974 /** 975 * returns the index of the next non-whitespace, non-comment token. 976 * returns -1 if no meaningful token is found. 977 */ 978 _proto.locateNextMeaningfulToken = function locateNextMeaningfulToken(startPosition) { 979 if (startPosition === void 0) { 980 startPosition = this.position + 1; 981 } 982 var searchPosition = startPosition; 983 while (searchPosition < this.tokens.length) { 984 if (WHITESPACE_EQUIV_TOKENS[this.tokens[searchPosition][_tokenize.FIELDS.TYPE]]) { 985 searchPosition++; 986 continue; 987 } else { 988 return searchPosition; 989 } 990 } 991 return -1; 992 }; 993 _createClass(Parser, [{ 994 key: "currToken", 995 get: function get() { 996 return this.tokens[this.position]; 997 } 998 }, { 999 key: "nextToken", 1000 get: function get() { 1001 return this.tokens[this.position + 1]; 1002 } 1003 }, { 1004 key: "prevToken", 1005 get: function get() { 1006 return this.tokens[this.position - 1]; 1007 } 1008 }]); 1009 return Parser; 1010}(); 1011exports["default"] = Parser; 1012module.exports = exports.default;