1 |
|
2 |
|
3 | const Comment = require('postcss/lib/comment');
|
4 | const Parser = require('postcss/lib/parser');
|
5 |
|
6 | const { isInlineComment } = require('./nodes/inline-comment');
|
7 | const { interpolation } = require('./nodes/interpolation');
|
8 | const { isMixinToken } = require('./nodes/mixin');
|
9 | const importNode = require('./nodes/import');
|
10 | const variableNode = require('./nodes/variable');
|
11 |
|
12 | const importantPattern = /(!\s*important)$/i;
|
13 |
|
14 | module.exports = class LessParser extends Parser {
|
15 | constructor(...args) {
|
16 | super(...args);
|
17 |
|
18 | this.lastNode = null;
|
19 | }
|
20 |
|
21 | atrule(token) {
|
22 | if (interpolation.bind(this)(token)) {
|
23 | return;
|
24 | }
|
25 |
|
26 | super.atrule(token);
|
27 | importNode(this.lastNode);
|
28 | variableNode(this.lastNode);
|
29 | }
|
30 |
|
31 | decl(...args) {
|
32 | super.decl(...args);
|
33 |
|
34 |
|
35 | const extendPattern = /extend\(.+\)/i;
|
36 |
|
37 | if (extendPattern.test(this.lastNode.value)) {
|
38 | this.lastNode.extend = true;
|
39 | }
|
40 | }
|
41 |
|
42 | each(tokens) {
|
43 |
|
44 | tokens[0][1] = ` ${tokens[0][1]}`;
|
45 |
|
46 | const firstParenIndex = tokens.findIndex((t) => t[0] === '(');
|
47 | const lastParen = tokens.reverse().find((t) => t[0] === ')');
|
48 | const lastParenIndex = tokens.reverse().indexOf(lastParen);
|
49 | const paramTokens = tokens.splice(firstParenIndex, lastParenIndex);
|
50 | const params = paramTokens.map((t) => t[1]).join('');
|
51 |
|
52 | for (const token of tokens.reverse()) {
|
53 | this.tokenizer.back(token);
|
54 | }
|
55 |
|
56 | this.atrule(this.tokenizer.nextToken());
|
57 | this.lastNode.function = true;
|
58 | this.lastNode.params = params;
|
59 | }
|
60 |
|
61 | init(node, line, column) {
|
62 | super.init(node, line, column);
|
63 | this.lastNode = node;
|
64 | }
|
65 |
|
66 | inlineComment(token) {
|
67 | const node = new Comment();
|
68 | const text = token[1].slice(2);
|
69 |
|
70 | this.init(node, token[2], token[3]);
|
71 |
|
72 | node.source.end = { line: token[4], column: token[5] };
|
73 | node.inline = true;
|
74 | node.raws.begin = '//';
|
75 |
|
76 | if (/^\s*$/.test(text)) {
|
77 | node.text = '';
|
78 | node.raws.left = text;
|
79 | node.raws.right = '';
|
80 | } else {
|
81 | const match = text.match(/^(\s*)([^]*[^\s])(\s*)$/);
|
82 | [, node.raws.left, node.text, node.raws.right] = match;
|
83 | }
|
84 | }
|
85 |
|
86 | mixin(tokens) {
|
87 | const [first] = tokens;
|
88 | const identifier = first[1].slice(0, 1);
|
89 | const bracketsIndex = tokens.findIndex((t) => t[0] === 'brackets');
|
90 | const firstParenIndex = tokens.findIndex((t) => t[0] === '(');
|
91 | let important = '';
|
92 |
|
93 |
|
94 | if ((bracketsIndex < 0 || bracketsIndex > 3) && firstParenIndex > 0) {
|
95 | const lastParenIndex = tokens.reduce((last, t, i) => (t[0] === ')' ? i : last));
|
96 |
|
97 | const contents = tokens.slice(firstParenIndex, lastParenIndex + firstParenIndex);
|
98 | const brackets = contents.map((t) => t[1]).join('');
|
99 | const [paren] = tokens.slice(firstParenIndex);
|
100 | const start = [paren[2], paren[3]];
|
101 | const [last] = tokens.slice(lastParenIndex, lastParenIndex + 1);
|
102 | const end = [last[2], last[3]];
|
103 | const newToken = ['brackets', brackets].concat(start, end);
|
104 |
|
105 | const tokensBefore = tokens.slice(0, firstParenIndex);
|
106 | const tokensAfter = tokens.slice(lastParenIndex + 1);
|
107 | tokens = tokensBefore;
|
108 | tokens.push(newToken);
|
109 | tokens = tokens.concat(tokensAfter);
|
110 | }
|
111 |
|
112 | const importantTokens = [];
|
113 |
|
114 | for (const token of tokens) {
|
115 | if (token[1] === '!' || importantTokens.length) {
|
116 | importantTokens.push(token);
|
117 | }
|
118 |
|
119 | if (token[1] === 'important') {
|
120 | break;
|
121 | }
|
122 | }
|
123 |
|
124 | if (importantTokens.length) {
|
125 | const [bangToken] = importantTokens;
|
126 | const bangIndex = tokens.indexOf(bangToken);
|
127 | const last = importantTokens[importantTokens.length - 1];
|
128 | const start = [bangToken[2], bangToken[3]];
|
129 | const end = [last[4], last[5]];
|
130 | const combined = importantTokens.map((t) => t[1]).join('');
|
131 | const newToken = ['word', combined].concat(start, end);
|
132 | tokens.splice(bangIndex, importantTokens.length, newToken);
|
133 | }
|
134 |
|
135 | const importantIndex = tokens.findIndex((t) => importantPattern.test(t[1]));
|
136 |
|
137 | if (importantIndex > 0) {
|
138 | [, important] = tokens[importantIndex];
|
139 | tokens.splice(importantIndex, 1);
|
140 | }
|
141 |
|
142 | for (const token of tokens.reverse()) {
|
143 | this.tokenizer.back(token);
|
144 | }
|
145 |
|
146 | this.atrule(this.tokenizer.nextToken());
|
147 | this.lastNode.mixin = true;
|
148 | this.lastNode.raws.identifier = identifier;
|
149 |
|
150 | if (important) {
|
151 | this.lastNode.important = true;
|
152 | this.lastNode.raws.important = important;
|
153 | }
|
154 | }
|
155 |
|
156 | other(token) {
|
157 | if (!isInlineComment.bind(this)(token)) {
|
158 | super.other(token);
|
159 | }
|
160 | }
|
161 |
|
162 | rule(tokens) {
|
163 | const last = tokens[tokens.length - 1];
|
164 | const prev = tokens[tokens.length - 2];
|
165 |
|
166 | if (prev[0] === 'at-word' && last[0] === '{') {
|
167 | this.tokenizer.back(last);
|
168 | if (interpolation.bind(this)(prev)) {
|
169 | const newToken = this.tokenizer.nextToken();
|
170 |
|
171 | tokens = tokens.slice(0, tokens.length - 2).concat([newToken]);
|
172 |
|
173 | for (const tokn of tokens.reverse()) {
|
174 | this.tokenizer.back(tokn);
|
175 | }
|
176 |
|
177 | return;
|
178 | }
|
179 | }
|
180 |
|
181 | super.rule(tokens);
|
182 |
|
183 |
|
184 | const extendPattern = /:extend\(.+\)/i;
|
185 |
|
186 | if (extendPattern.test(this.lastNode.selector)) {
|
187 | this.lastNode.extend = true;
|
188 | }
|
189 | }
|
190 |
|
191 | unknownWord(tokens) {
|
192 |
|
193 |
|
194 |
|
195 | const [first] = tokens;
|
196 |
|
197 |
|
198 | if (tokens[0][1] === 'each' && tokens[1][0] === '(') {
|
199 | this.each(tokens);
|
200 | return;
|
201 | }
|
202 |
|
203 |
|
204 | if (isMixinToken(first)) {
|
205 | this.mixin(tokens);
|
206 | return;
|
207 | }
|
208 |
|
209 | super.unknownWord(tokens);
|
210 | }
|
211 | };
|