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