1 | var Marker = require('./marker');
|
---|
2 | var Token = require('./token');
|
---|
3 |
|
---|
4 | var formatPosition = require('../utils/format-position');
|
---|
5 |
|
---|
6 | var Level = {
|
---|
7 | BLOCK: 'block',
|
---|
8 | COMMENT: 'comment',
|
---|
9 | DOUBLE_QUOTE: 'double-quote',
|
---|
10 | RULE: 'rule',
|
---|
11 | SINGLE_QUOTE: 'single-quote'
|
---|
12 | };
|
---|
13 |
|
---|
14 | var AT_RULES = [
|
---|
15 | '@charset',
|
---|
16 | '@import'
|
---|
17 | ];
|
---|
18 |
|
---|
19 | var BLOCK_RULES = [
|
---|
20 | '@-moz-document',
|
---|
21 | '@document',
|
---|
22 | '@-moz-keyframes',
|
---|
23 | '@-ms-keyframes',
|
---|
24 | '@-o-keyframes',
|
---|
25 | '@-webkit-keyframes',
|
---|
26 | '@keyframes',
|
---|
27 | '@media',
|
---|
28 | '@supports',
|
---|
29 | '@container',
|
---|
30 | '@layer'
|
---|
31 | ];
|
---|
32 |
|
---|
33 | var IGNORE_END_COMMENT_PATTERN = /\/\* clean-css ignore:end \*\/$/;
|
---|
34 | var IGNORE_START_COMMENT_PATTERN = /^\/\* clean-css ignore:start \*\//;
|
---|
35 |
|
---|
36 | var PAGE_MARGIN_BOXES = [
|
---|
37 | '@bottom-center',
|
---|
38 | '@bottom-left',
|
---|
39 | '@bottom-left-corner',
|
---|
40 | '@bottom-right',
|
---|
41 | '@bottom-right-corner',
|
---|
42 | '@left-bottom',
|
---|
43 | '@left-middle',
|
---|
44 | '@left-top',
|
---|
45 | '@right-bottom',
|
---|
46 | '@right-middle',
|
---|
47 | '@right-top',
|
---|
48 | '@top-center',
|
---|
49 | '@top-left',
|
---|
50 | '@top-left-corner',
|
---|
51 | '@top-right',
|
---|
52 | '@top-right-corner'
|
---|
53 | ];
|
---|
54 |
|
---|
55 | var EXTRA_PAGE_BOXES = [
|
---|
56 | '@footnote',
|
---|
57 | '@footnotes',
|
---|
58 | '@left',
|
---|
59 | '@page-float-bottom',
|
---|
60 | '@page-float-top',
|
---|
61 | '@right'
|
---|
62 | ];
|
---|
63 |
|
---|
64 | var REPEAT_PATTERN = /^\[\s{0,31}\d+\s{0,31}\]$/;
|
---|
65 | var TAIL_BROKEN_VALUE_PATTERN = /([^}])\}*$/;
|
---|
66 | var RULE_WORD_SEPARATOR_PATTERN = /[\s(]/;
|
---|
67 |
|
---|
68 | function tokenize(source, externalContext) {
|
---|
69 | var internalContext = {
|
---|
70 | level: Level.BLOCK,
|
---|
71 | position: {
|
---|
72 | source: externalContext.source || undefined,
|
---|
73 | line: 1,
|
---|
74 | column: 0,
|
---|
75 | index: 0
|
---|
76 | }
|
---|
77 | };
|
---|
78 |
|
---|
79 | return intoTokens(source, externalContext, internalContext, false);
|
---|
80 | }
|
---|
81 |
|
---|
82 | function intoTokens(source, externalContext, internalContext, isNested) {
|
---|
83 | var allTokens = [];
|
---|
84 | var newTokens = allTokens;
|
---|
85 | var lastToken;
|
---|
86 | var ruleToken;
|
---|
87 | var ruleTokens = [];
|
---|
88 | var propertyToken;
|
---|
89 | var metadata;
|
---|
90 | var metadatas = [];
|
---|
91 | var level = internalContext.level;
|
---|
92 | var levels = [];
|
---|
93 | var buffer = [];
|
---|
94 | var buffers = [];
|
---|
95 | var isBufferEmpty = true;
|
---|
96 | var serializedBuffer;
|
---|
97 | var serializedBufferPart;
|
---|
98 | var roundBracketLevel = 0;
|
---|
99 | var isQuoted;
|
---|
100 | var isSpace;
|
---|
101 | var isNewLineNix;
|
---|
102 | var isNewLineWin;
|
---|
103 | var isCarriageReturn;
|
---|
104 | var isCommentStart;
|
---|
105 | var wasCommentStart = false;
|
---|
106 | var isCommentEnd;
|
---|
107 | var wasCommentEnd = false;
|
---|
108 | var isCommentEndMarker;
|
---|
109 | var isEscaped;
|
---|
110 | var wasEscaped = false;
|
---|
111 | var characterWithNoSpecialMeaning;
|
---|
112 | var isPreviousDash = false;
|
---|
113 | var isVariable = false;
|
---|
114 | var isRaw = false;
|
---|
115 | var seekingValue = false;
|
---|
116 | var seekingPropertyBlockClosing = false;
|
---|
117 | var position = internalContext.position;
|
---|
118 | var lastCommentStartAt;
|
---|
119 |
|
---|
120 | for (; position.index < source.length; position.index++) {
|
---|
121 | var character = source[position.index];
|
---|
122 |
|
---|
123 | isQuoted = level == Level.SINGLE_QUOTE || level == Level.DOUBLE_QUOTE;
|
---|
124 | isSpace = character == Marker.SPACE || character == Marker.TAB;
|
---|
125 | isNewLineNix = character == Marker.NEW_LINE_NIX;
|
---|
126 | isNewLineWin = character == Marker.NEW_LINE_NIX
|
---|
127 | && source[position.index - 1] == Marker.CARRIAGE_RETURN;
|
---|
128 | isCarriageReturn = character == Marker.CARRIAGE_RETURN
|
---|
129 | && source[position.index + 1] && source[position.index + 1] != Marker.NEW_LINE_NIX;
|
---|
130 | isCommentStart = !wasCommentEnd
|
---|
131 | && level != Level.COMMENT && !isQuoted
|
---|
132 | && character == Marker.ASTERISK && source[position.index - 1] == Marker.FORWARD_SLASH;
|
---|
133 | isCommentEndMarker = !wasCommentStart
|
---|
134 | && !isQuoted && character == Marker.FORWARD_SLASH
|
---|
135 | && source[position.index - 1] == Marker.ASTERISK;
|
---|
136 | isCommentEnd = level == Level.COMMENT && isCommentEndMarker;
|
---|
137 | characterWithNoSpecialMeaning = !isSpace && !isCarriageReturn && (character >= 'A' && character <= 'Z' || character >= 'a' && character <= 'z' || character >= '0' && character <= '9' || character == '-');
|
---|
138 | isVariable = isVariable || (level != Level.COMMENT && !seekingValue && isPreviousDash && character === '-' && buffer.length === 1);
|
---|
139 | isPreviousDash = character === '-';
|
---|
140 | roundBracketLevel = Math.max(roundBracketLevel, 0);
|
---|
141 |
|
---|
142 | metadata = isBufferEmpty
|
---|
143 | ? [position.line, position.column, position.source]
|
---|
144 | : metadata;
|
---|
145 |
|
---|
146 | if (isEscaped) {
|
---|
147 | // previous character was a backslash
|
---|
148 | buffer.push(character);
|
---|
149 | isBufferEmpty = false;
|
---|
150 | } else if (characterWithNoSpecialMeaning) {
|
---|
151 | // it's just an alphanumeric character or a hyphen (part of any rule or property name) so let's end it quickly
|
---|
152 | buffer.push(character);
|
---|
153 | isBufferEmpty = false;
|
---|
154 | } else if ((isSpace || isNewLineNix && !isNewLineWin) && (isQuoted || level == Level.COMMENT)) {
|
---|
155 | buffer.push(character);
|
---|
156 | isBufferEmpty = false;
|
---|
157 | } else if ((isSpace || isNewLineNix && !isNewLineWin) && isBufferEmpty) {
|
---|
158 | // noop
|
---|
159 | } else if (!isCommentEnd && level == Level.COMMENT) {
|
---|
160 | buffer.push(character);
|
---|
161 | isBufferEmpty = false;
|
---|
162 | } else if (!isCommentStart && !isCommentEnd && isRaw) {
|
---|
163 | buffer.push(character);
|
---|
164 | isBufferEmpty = false;
|
---|
165 | } else if (isCommentStart
|
---|
166 | && isVariable
|
---|
167 | && (level == Level.BLOCK || level == Level.RULE) && buffer.length > 1) {
|
---|
168 | // comment start within a variable, e.g. var(/*<--
|
---|
169 | buffer.push(character);
|
---|
170 | isBufferEmpty = false;
|
---|
171 |
|
---|
172 | levels.push(level);
|
---|
173 | level = Level.COMMENT;
|
---|
174 | } else if (isCommentStart && (level == Level.BLOCK || level == Level.RULE) && buffer.length > 1) {
|
---|
175 | // comment start within block preceded by some content, e.g. div/*<--
|
---|
176 | metadatas.push(metadata);
|
---|
177 | buffer.push(character);
|
---|
178 | buffers.push(buffer.slice(0, -2));
|
---|
179 | isBufferEmpty = false;
|
---|
180 |
|
---|
181 | buffer = buffer.slice(-2);
|
---|
182 | metadata = [position.line, position.column - 1, position.source];
|
---|
183 |
|
---|
184 | levels.push(level);
|
---|
185 | level = Level.COMMENT;
|
---|
186 | } else if (isCommentStart) {
|
---|
187 | // comment start, e.g. /*<--
|
---|
188 | levels.push(level);
|
---|
189 | level = Level.COMMENT;
|
---|
190 | buffer.push(character);
|
---|
191 | isBufferEmpty = false;
|
---|
192 | } else if (isCommentEnd && isVariable) {
|
---|
193 | // comment end within a variable, e.g. var(/*!*/<--
|
---|
194 | buffer.push(character);
|
---|
195 | level = levels.pop();
|
---|
196 | } else if (isCommentEnd && isIgnoreStartComment(buffer)) {
|
---|
197 | // ignore:start comment end, e.g. /* clean-css ignore:start */<--
|
---|
198 | serializedBuffer = buffer.join('').trim() + character;
|
---|
199 | lastToken = [
|
---|
200 | Token.COMMENT,
|
---|
201 | serializedBuffer,
|
---|
202 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
203 | ];
|
---|
204 | newTokens.push(lastToken);
|
---|
205 |
|
---|
206 | isRaw = true;
|
---|
207 | metadata = metadatas.pop() || null;
|
---|
208 | buffer = buffers.pop() || [];
|
---|
209 | isBufferEmpty = buffer.length === 0;
|
---|
210 | } else if (isCommentEnd && isIgnoreEndComment(buffer)) {
|
---|
211 | // ignore:start comment end, e.g. /* clean-css ignore:end */<--
|
---|
212 | serializedBuffer = buffer.join('') + character;
|
---|
213 | lastCommentStartAt = serializedBuffer.lastIndexOf(Marker.FORWARD_SLASH + Marker.ASTERISK);
|
---|
214 |
|
---|
215 | serializedBufferPart = serializedBuffer.substring(0, lastCommentStartAt);
|
---|
216 | lastToken = [
|
---|
217 | Token.RAW,
|
---|
218 | serializedBufferPart,
|
---|
219 | [originalMetadata(metadata, serializedBufferPart, externalContext)]
|
---|
220 | ];
|
---|
221 | newTokens.push(lastToken);
|
---|
222 |
|
---|
223 | serializedBufferPart = serializedBuffer.substring(lastCommentStartAt);
|
---|
224 | metadata = [position.line, position.column - serializedBufferPart.length + 1, position.source];
|
---|
225 | lastToken = [
|
---|
226 | Token.COMMENT,
|
---|
227 | serializedBufferPart,
|
---|
228 | [originalMetadata(metadata, serializedBufferPart, externalContext)]
|
---|
229 | ];
|
---|
230 | newTokens.push(lastToken);
|
---|
231 |
|
---|
232 | isRaw = false;
|
---|
233 | level = levels.pop();
|
---|
234 | metadata = metadatas.pop() || null;
|
---|
235 | buffer = buffers.pop() || [];
|
---|
236 | isBufferEmpty = buffer.length === 0;
|
---|
237 | } else if (isCommentEnd) {
|
---|
238 | // comment end, e.g. /* comment */<--
|
---|
239 | serializedBuffer = buffer.join('').trim() + character;
|
---|
240 | lastToken = [
|
---|
241 | Token.COMMENT,
|
---|
242 | serializedBuffer,
|
---|
243 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
244 | ];
|
---|
245 | newTokens.push(lastToken);
|
---|
246 |
|
---|
247 | level = levels.pop();
|
---|
248 | metadata = metadatas.pop() || null;
|
---|
249 | buffer = buffers.pop() || [];
|
---|
250 | isBufferEmpty = buffer.length === 0;
|
---|
251 | } else if (isCommentEndMarker && source[position.index + 1] != Marker.ASTERISK) {
|
---|
252 | externalContext.warnings.push('Unexpected \'*/\' at ' + formatPosition([position.line, position.column, position.source]) + '.');
|
---|
253 | buffer = [];
|
---|
254 | isBufferEmpty = true;
|
---|
255 | } else if (character == Marker.SINGLE_QUOTE && !isQuoted) {
|
---|
256 | // single quotation start, e.g. a[href^='https<--
|
---|
257 | levels.push(level);
|
---|
258 | level = Level.SINGLE_QUOTE;
|
---|
259 | buffer.push(character);
|
---|
260 | isBufferEmpty = false;
|
---|
261 | } else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) {
|
---|
262 | // single quotation end, e.g. a[href^='https'<--
|
---|
263 | level = levels.pop();
|
---|
264 | buffer.push(character);
|
---|
265 | isBufferEmpty = false;
|
---|
266 | } else if (character == Marker.DOUBLE_QUOTE && !isQuoted) {
|
---|
267 | // double quotation start, e.g. a[href^="<--
|
---|
268 | levels.push(level);
|
---|
269 | level = Level.DOUBLE_QUOTE;
|
---|
270 | buffer.push(character);
|
---|
271 | isBufferEmpty = false;
|
---|
272 | } else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) {
|
---|
273 | // double quotation end, e.g. a[href^="https"<--
|
---|
274 | level = levels.pop();
|
---|
275 | buffer.push(character);
|
---|
276 | isBufferEmpty = false;
|
---|
277 | } else if (character != Marker.CLOSE_ROUND_BRACKET
|
---|
278 | && character != Marker.OPEN_ROUND_BRACKET
|
---|
279 | && level != Level.COMMENT && !isQuoted && roundBracketLevel > 0) {
|
---|
280 | // character inside any function, e.g. hsla(.<--
|
---|
281 | buffer.push(character);
|
---|
282 | isBufferEmpty = false;
|
---|
283 | } else if (character == Marker.OPEN_ROUND_BRACKET
|
---|
284 | && !isQuoted && level != Level.COMMENT
|
---|
285 | && !seekingValue) {
|
---|
286 | // round open bracket, e.g. @import url(<--
|
---|
287 | buffer.push(character);
|
---|
288 | isBufferEmpty = false;
|
---|
289 |
|
---|
290 | roundBracketLevel++;
|
---|
291 | } else if (character == Marker.CLOSE_ROUND_BRACKET
|
---|
292 | && !isQuoted
|
---|
293 | && level != Level.COMMENT
|
---|
294 | && !seekingValue) {
|
---|
295 | // round open bracket, e.g. @import url(test.css)<--
|
---|
296 | buffer.push(character);
|
---|
297 | isBufferEmpty = false;
|
---|
298 |
|
---|
299 | roundBracketLevel--;
|
---|
300 | } else if (character == Marker.SEMICOLON && level == Level.BLOCK && buffer[0] == Marker.AT) {
|
---|
301 | // semicolon ending rule at block level, e.g. @import '...';<--
|
---|
302 | serializedBuffer = buffer.join('').trim();
|
---|
303 | allTokens.push([
|
---|
304 | Token.AT_RULE,
|
---|
305 | serializedBuffer,
|
---|
306 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
307 | ]);
|
---|
308 |
|
---|
309 | buffer = [];
|
---|
310 | isBufferEmpty = true;
|
---|
311 | } else if (character == Marker.COMMA && level == Level.BLOCK && ruleToken) {
|
---|
312 | // comma separator at block level, e.g. a,div,<--
|
---|
313 | serializedBuffer = buffer.join('').trim();
|
---|
314 | ruleToken[1].push([
|
---|
315 | tokenScopeFrom(ruleToken[0]),
|
---|
316 | serializedBuffer,
|
---|
317 | [originalMetadata(metadata, serializedBuffer, externalContext, ruleToken[1].length)]
|
---|
318 | ]);
|
---|
319 |
|
---|
320 | buffer = [];
|
---|
321 | isBufferEmpty = true;
|
---|
322 | } else if (character == Marker.COMMA && level == Level.BLOCK && tokenTypeFrom(buffer) == Token.AT_RULE) {
|
---|
323 | // comma separator at block level, e.g. @import url(...) screen,<--
|
---|
324 | // keep iterating as end semicolon will create the token
|
---|
325 | buffer.push(character);
|
---|
326 | isBufferEmpty = false;
|
---|
327 | } else if (character == Marker.COMMA && level == Level.BLOCK) {
|
---|
328 | // comma separator at block level, e.g. a,<--
|
---|
329 | ruleToken = [tokenTypeFrom(buffer), [], []];
|
---|
330 | serializedBuffer = buffer.join('').trim();
|
---|
331 | ruleToken[1].push([
|
---|
332 | tokenScopeFrom(ruleToken[0]),
|
---|
333 | serializedBuffer,
|
---|
334 | [originalMetadata(metadata, serializedBuffer, externalContext, 0)]
|
---|
335 | ]);
|
---|
336 |
|
---|
337 | buffer = [];
|
---|
338 | isBufferEmpty = true;
|
---|
339 | } else if (character == Marker.OPEN_CURLY_BRACKET
|
---|
340 | && level == Level.BLOCK
|
---|
341 | && ruleToken
|
---|
342 | && ruleToken[0] == Token.NESTED_BLOCK) {
|
---|
343 | // open brace opening at-rule at block level, e.g. @media{<--
|
---|
344 | serializedBuffer = buffer.join('').trim();
|
---|
345 | ruleToken[1].push([
|
---|
346 | Token.NESTED_BLOCK_SCOPE,
|
---|
347 | serializedBuffer,
|
---|
348 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
349 | ]);
|
---|
350 | allTokens.push(ruleToken);
|
---|
351 |
|
---|
352 | levels.push(level);
|
---|
353 | position.column++;
|
---|
354 | position.index++;
|
---|
355 | buffer = [];
|
---|
356 | isBufferEmpty = true;
|
---|
357 |
|
---|
358 | ruleToken[2] = intoTokens(source, externalContext, internalContext, true);
|
---|
359 | ruleToken = null;
|
---|
360 | } else if (character == Marker.OPEN_CURLY_BRACKET
|
---|
361 | && level == Level.BLOCK
|
---|
362 | && tokenTypeFrom(buffer) == Token.NESTED_BLOCK) {
|
---|
363 | // open brace opening at-rule at block level, e.g. @media{<--
|
---|
364 | serializedBuffer = buffer.join('').trim();
|
---|
365 | ruleToken = ruleToken || [Token.NESTED_BLOCK, [], []];
|
---|
366 | ruleToken[1].push([
|
---|
367 | Token.NESTED_BLOCK_SCOPE,
|
---|
368 | serializedBuffer,
|
---|
369 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
370 | ]);
|
---|
371 | allTokens.push(ruleToken);
|
---|
372 |
|
---|
373 | levels.push(level);
|
---|
374 | position.column++;
|
---|
375 | position.index++;
|
---|
376 | buffer = [];
|
---|
377 | isBufferEmpty = true;
|
---|
378 | isVariable = false;
|
---|
379 |
|
---|
380 | ruleToken[2] = intoTokens(source, externalContext, internalContext, true);
|
---|
381 | ruleToken = null;
|
---|
382 | } else if (character == Marker.OPEN_CURLY_BRACKET && level == Level.BLOCK) {
|
---|
383 | // open brace opening rule at block level, e.g. div{<--
|
---|
384 | serializedBuffer = buffer.join('').trim();
|
---|
385 | ruleToken = ruleToken || [tokenTypeFrom(buffer), [], []];
|
---|
386 | ruleToken[1].push([
|
---|
387 | tokenScopeFrom(ruleToken[0]),
|
---|
388 | serializedBuffer,
|
---|
389 | [originalMetadata(metadata, serializedBuffer, externalContext, ruleToken[1].length)]
|
---|
390 | ]);
|
---|
391 | newTokens = ruleToken[2];
|
---|
392 | allTokens.push(ruleToken);
|
---|
393 |
|
---|
394 | levels.push(level);
|
---|
395 | level = Level.RULE;
|
---|
396 | buffer = [];
|
---|
397 | isBufferEmpty = true;
|
---|
398 | } else if (character == Marker.OPEN_CURLY_BRACKET && level == Level.RULE && seekingValue) {
|
---|
399 | // open brace opening rule at rule level, e.g. div{--variable:{<--
|
---|
400 | ruleTokens.push(ruleToken);
|
---|
401 | ruleToken = [Token.PROPERTY_BLOCK, []];
|
---|
402 | propertyToken.push(ruleToken);
|
---|
403 | newTokens = ruleToken[1];
|
---|
404 |
|
---|
405 | levels.push(level);
|
---|
406 | level = Level.RULE;
|
---|
407 | seekingValue = false;
|
---|
408 | } else if (character == Marker.OPEN_CURLY_BRACKET && level == Level.RULE && isPageMarginBox(buffer)) {
|
---|
409 | // open brace opening page-margin box at rule level, e.g. @page{@top-center{<--
|
---|
410 | serializedBuffer = buffer.join('').trim();
|
---|
411 | ruleTokens.push(ruleToken);
|
---|
412 | ruleToken = [Token.AT_RULE_BLOCK, [], []];
|
---|
413 | ruleToken[1].push([
|
---|
414 | Token.AT_RULE_BLOCK_SCOPE,
|
---|
415 | serializedBuffer,
|
---|
416 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
417 | ]);
|
---|
418 | newTokens.push(ruleToken);
|
---|
419 | newTokens = ruleToken[2];
|
---|
420 |
|
---|
421 | levels.push(level);
|
---|
422 | level = Level.RULE;
|
---|
423 | buffer = [];
|
---|
424 | isBufferEmpty = true;
|
---|
425 | } else if (character == Marker.COLON && level == Level.RULE && !seekingValue) {
|
---|
426 | // colon at rule level, e.g. a{color:<--
|
---|
427 | serializedBuffer = buffer.join('').trim();
|
---|
428 | propertyToken = [
|
---|
429 | Token.PROPERTY,
|
---|
430 | [
|
---|
431 | Token.PROPERTY_NAME,
|
---|
432 | serializedBuffer,
|
---|
433 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
434 | ]
|
---|
435 | ];
|
---|
436 | newTokens.push(propertyToken);
|
---|
437 |
|
---|
438 | seekingValue = true;
|
---|
439 | buffer = [];
|
---|
440 | isBufferEmpty = true;
|
---|
441 | } else if (character == Marker.SEMICOLON
|
---|
442 | && level == Level.RULE
|
---|
443 | && propertyToken
|
---|
444 | && ruleTokens.length > 0
|
---|
445 | && !isBufferEmpty
|
---|
446 | && buffer[0] == Marker.AT) {
|
---|
447 | // semicolon at rule level for at-rule, e.g. a{--color:{@apply(--other-color);<--
|
---|
448 | serializedBuffer = buffer.join('').trim();
|
---|
449 | ruleToken[1].push([
|
---|
450 | Token.AT_RULE,
|
---|
451 | serializedBuffer,
|
---|
452 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
453 | ]);
|
---|
454 |
|
---|
455 | buffer = [];
|
---|
456 | isBufferEmpty = true;
|
---|
457 | } else if (character == Marker.SEMICOLON && level == Level.RULE && propertyToken && !isBufferEmpty) {
|
---|
458 | // semicolon at rule level, e.g. a{color:red;<--
|
---|
459 | serializedBuffer = buffer.join('').trim();
|
---|
460 | propertyToken.push([
|
---|
461 | Token.PROPERTY_VALUE,
|
---|
462 | serializedBuffer,
|
---|
463 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
464 | ]);
|
---|
465 |
|
---|
466 | propertyToken = null;
|
---|
467 | seekingValue = false;
|
---|
468 | buffer = [];
|
---|
469 | isBufferEmpty = true;
|
---|
470 | isVariable = false;
|
---|
471 | } else if (character == Marker.SEMICOLON
|
---|
472 | && level == Level.RULE
|
---|
473 | && propertyToken
|
---|
474 | && isBufferEmpty
|
---|
475 | && isVariable
|
---|
476 | && !propertyToken[2]) {
|
---|
477 | // semicolon after empty variable value at rule level, e.g. a{--color: ;<--
|
---|
478 | propertyToken.push([Token.PROPERTY_VALUE, ' ', [originalMetadata(metadata, ' ', externalContext)]]);
|
---|
479 | isVariable = false;
|
---|
480 | propertyToken = null;
|
---|
481 | seekingValue = false;
|
---|
482 | } else if (character == Marker.SEMICOLON && level == Level.RULE && propertyToken && isBufferEmpty) {
|
---|
483 | // semicolon after bracketed value at rule level, e.g. a{color:rgb(...);<--
|
---|
484 | propertyToken = null;
|
---|
485 | seekingValue = false;
|
---|
486 | } else if (character == Marker.SEMICOLON
|
---|
487 | && level == Level.RULE
|
---|
488 | && !isBufferEmpty
|
---|
489 | && buffer[0] == Marker.AT) {
|
---|
490 | // semicolon for at-rule at rule level, e.g. a{@apply(--variable);<--
|
---|
491 | serializedBuffer = buffer.join('');
|
---|
492 | newTokens.push([
|
---|
493 | Token.AT_RULE,
|
---|
494 | serializedBuffer,
|
---|
495 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
496 | ]);
|
---|
497 |
|
---|
498 | seekingValue = false;
|
---|
499 | buffer = [];
|
---|
500 | isBufferEmpty = true;
|
---|
501 | } else if (character == Marker.SEMICOLON && level == Level.RULE && seekingPropertyBlockClosing) {
|
---|
502 | // close brace after a property block at rule level, e.g. a{--custom:{color:red;};<--
|
---|
503 | seekingPropertyBlockClosing = false;
|
---|
504 | buffer = [];
|
---|
505 | isBufferEmpty = true;
|
---|
506 | } else if (character == Marker.SEMICOLON && level == Level.RULE && isBufferEmpty) {
|
---|
507 | // stray semicolon at rule level, e.g. a{;<--
|
---|
508 | // noop
|
---|
509 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
510 | && level == Level.RULE
|
---|
511 | && propertyToken
|
---|
512 | && seekingValue
|
---|
513 | && !isBufferEmpty && ruleTokens.length > 0) {
|
---|
514 | // close brace at rule level, e.g. a{--color:{color:red}<--
|
---|
515 | serializedBuffer = buffer.join('');
|
---|
516 | propertyToken.push([
|
---|
517 | Token.PROPERTY_VALUE,
|
---|
518 | serializedBuffer,
|
---|
519 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
520 | ]);
|
---|
521 | propertyToken = null;
|
---|
522 | ruleToken = ruleTokens.pop();
|
---|
523 | newTokens = ruleToken[2];
|
---|
524 |
|
---|
525 | level = levels.pop();
|
---|
526 | seekingValue = false;
|
---|
527 | buffer = [];
|
---|
528 | isBufferEmpty = true;
|
---|
529 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
530 | && level == Level.RULE
|
---|
531 | && propertyToken
|
---|
532 | && !isBufferEmpty
|
---|
533 | && buffer[0] == Marker.AT
|
---|
534 | && ruleTokens.length > 0) {
|
---|
535 | // close brace at rule level for at-rule, e.g. a{--color:{@apply(--other-color)}<--
|
---|
536 | serializedBuffer = buffer.join('');
|
---|
537 | ruleToken[1].push([
|
---|
538 | Token.AT_RULE,
|
---|
539 | serializedBuffer,
|
---|
540 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
541 | ]);
|
---|
542 | propertyToken = null;
|
---|
543 | ruleToken = ruleTokens.pop();
|
---|
544 | newTokens = ruleToken[2];
|
---|
545 |
|
---|
546 | level = levels.pop();
|
---|
547 | seekingValue = false;
|
---|
548 | buffer = [];
|
---|
549 | isBufferEmpty = true;
|
---|
550 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
551 | && level == Level.RULE
|
---|
552 | && propertyToken
|
---|
553 | && ruleTokens.length > 0) {
|
---|
554 | // close brace at rule level after space, e.g. a{--color:{color:red }<--
|
---|
555 | propertyToken = null;
|
---|
556 | ruleToken = ruleTokens.pop();
|
---|
557 | newTokens = ruleToken[2];
|
---|
558 |
|
---|
559 | level = levels.pop();
|
---|
560 | seekingValue = false;
|
---|
561 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
562 | && level == Level.RULE
|
---|
563 | && propertyToken
|
---|
564 | && !isBufferEmpty) {
|
---|
565 | // close brace at rule level, e.g. a{color:red}<--
|
---|
566 | serializedBuffer = buffer.join('');
|
---|
567 | propertyToken.push([
|
---|
568 | Token.PROPERTY_VALUE,
|
---|
569 | serializedBuffer,
|
---|
570 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
571 | ]);
|
---|
572 | propertyToken = null;
|
---|
573 | ruleToken = ruleTokens.pop();
|
---|
574 | newTokens = allTokens;
|
---|
575 |
|
---|
576 | level = levels.pop();
|
---|
577 | seekingValue = false;
|
---|
578 | buffer = [];
|
---|
579 | isBufferEmpty = true;
|
---|
580 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
581 | && level == Level.RULE
|
---|
582 | && !isBufferEmpty
|
---|
583 | && buffer[0] == Marker.AT) {
|
---|
584 | // close brace after at-rule at rule level, e.g. a{@apply(--variable)}<--
|
---|
585 | propertyToken = null;
|
---|
586 | ruleToken = null;
|
---|
587 | serializedBuffer = buffer.join('').trim();
|
---|
588 | newTokens.push([
|
---|
589 | Token.AT_RULE,
|
---|
590 | serializedBuffer,
|
---|
591 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
592 | ]);
|
---|
593 | newTokens = allTokens;
|
---|
594 |
|
---|
595 | level = levels.pop();
|
---|
596 | seekingValue = false;
|
---|
597 | buffer = [];
|
---|
598 | isBufferEmpty = true;
|
---|
599 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
600 | && level == Level.RULE
|
---|
601 | && levels[levels.length - 1] == Level.RULE) {
|
---|
602 | // close brace after a property block at rule level, e.g. a{--custom:{color:red;}<--
|
---|
603 | propertyToken = null;
|
---|
604 | ruleToken = ruleTokens.pop();
|
---|
605 | newTokens = ruleToken[2];
|
---|
606 |
|
---|
607 | level = levels.pop();
|
---|
608 | seekingValue = false;
|
---|
609 | seekingPropertyBlockClosing = true;
|
---|
610 | buffer = [];
|
---|
611 | isBufferEmpty = true;
|
---|
612 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
613 | && level == Level.RULE
|
---|
614 | && isVariable
|
---|
615 | && propertyToken
|
---|
616 | && !propertyToken[2]) {
|
---|
617 | // close brace after an empty variable declaration inside a rule, e.g. a{--color: }<--
|
---|
618 | propertyToken.push([Token.PROPERTY_VALUE, ' ', [originalMetadata(metadata, ' ', externalContext)]]);
|
---|
619 | isVariable = false;
|
---|
620 | propertyToken = null;
|
---|
621 | ruleToken = null;
|
---|
622 | newTokens = allTokens;
|
---|
623 |
|
---|
624 | level = levels.pop();
|
---|
625 | seekingValue = false;
|
---|
626 | isVariable = false;
|
---|
627 | } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.RULE) {
|
---|
628 | // close brace after a rule, e.g. a{color:red;}<--
|
---|
629 | propertyToken = null;
|
---|
630 | ruleToken = null;
|
---|
631 | newTokens = allTokens;
|
---|
632 |
|
---|
633 | level = levels.pop();
|
---|
634 | seekingValue = false;
|
---|
635 | isVariable = false;
|
---|
636 | } else if (character == Marker.CLOSE_CURLY_BRACKET
|
---|
637 | && level == Level.BLOCK
|
---|
638 | && !isNested
|
---|
639 | && position.index <= source.length - 1) {
|
---|
640 | // stray close brace at block level, e.g. a{color:red}color:blue}<--
|
---|
641 | externalContext.warnings.push('Unexpected \'}\' at ' + formatPosition([position.line, position.column, position.source]) + '.');
|
---|
642 | buffer.push(character);
|
---|
643 | isBufferEmpty = false;
|
---|
644 | } else if (character == Marker.CLOSE_CURLY_BRACKET && level == Level.BLOCK) {
|
---|
645 | // close brace at block level, e.g. @media screen {...}<--
|
---|
646 | break;
|
---|
647 | } else if (character == Marker.OPEN_ROUND_BRACKET && level == Level.RULE && seekingValue) {
|
---|
648 | // round open bracket, e.g. a{color:hsla(<--
|
---|
649 | buffer.push(character);
|
---|
650 | isBufferEmpty = false;
|
---|
651 | roundBracketLevel++;
|
---|
652 | } else if (character == Marker.CLOSE_ROUND_BRACKET
|
---|
653 | && level == Level.RULE
|
---|
654 | && seekingValue
|
---|
655 | && roundBracketLevel == 1) {
|
---|
656 | // round close bracket, e.g. a{color:hsla(0,0%,0%)<--
|
---|
657 | buffer.push(character);
|
---|
658 | isBufferEmpty = false;
|
---|
659 | serializedBuffer = buffer.join('').trim();
|
---|
660 | propertyToken.push([
|
---|
661 | Token.PROPERTY_VALUE,
|
---|
662 | serializedBuffer,
|
---|
663 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
664 | ]);
|
---|
665 |
|
---|
666 | roundBracketLevel--;
|
---|
667 | buffer = [];
|
---|
668 | isBufferEmpty = true;
|
---|
669 | isVariable = false;
|
---|
670 | } else if (character == Marker.CLOSE_ROUND_BRACKET && level == Level.RULE && seekingValue) {
|
---|
671 | // round close bracket within other brackets, e.g. a{width:calc((10rem / 2)<--
|
---|
672 | buffer.push(character);
|
---|
673 | isBufferEmpty = false;
|
---|
674 | isVariable = false;
|
---|
675 | roundBracketLevel--;
|
---|
676 | } else if (character == Marker.FORWARD_SLASH
|
---|
677 | && source[position.index + 1] != Marker.ASTERISK
|
---|
678 | && level == Level.RULE
|
---|
679 | && seekingValue
|
---|
680 | && !isBufferEmpty) {
|
---|
681 | // forward slash within a property, e.g. a{background:url(image.png) 0 0/<--
|
---|
682 | serializedBuffer = buffer.join('').trim();
|
---|
683 | propertyToken.push([
|
---|
684 | Token.PROPERTY_VALUE,
|
---|
685 | serializedBuffer,
|
---|
686 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
687 | ]);
|
---|
688 | propertyToken.push([
|
---|
689 | Token.PROPERTY_VALUE,
|
---|
690 | character,
|
---|
691 | [[position.line, position.column, position.source]]
|
---|
692 | ]);
|
---|
693 |
|
---|
694 | buffer = [];
|
---|
695 | isBufferEmpty = true;
|
---|
696 | } else if (character == Marker.FORWARD_SLASH
|
---|
697 | && source[position.index + 1] != Marker.ASTERISK
|
---|
698 | && level == Level.RULE
|
---|
699 | && seekingValue) {
|
---|
700 | // forward slash within a property after space, e.g. a{background:url(image.png) 0 0 /<--
|
---|
701 | propertyToken.push([
|
---|
702 | Token.PROPERTY_VALUE,
|
---|
703 | character,
|
---|
704 | [[position.line, position.column, position.source]]
|
---|
705 | ]);
|
---|
706 |
|
---|
707 | buffer = [];
|
---|
708 | isBufferEmpty = true;
|
---|
709 | } else if (character == Marker.COMMA && level == Level.RULE && seekingValue && !isBufferEmpty) {
|
---|
710 | // comma within a property, e.g. a{background:url(image.png),<--
|
---|
711 | serializedBuffer = buffer.join('').trim();
|
---|
712 | propertyToken.push([
|
---|
713 | Token.PROPERTY_VALUE,
|
---|
714 | serializedBuffer,
|
---|
715 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
716 | ]);
|
---|
717 | propertyToken.push([
|
---|
718 | Token.PROPERTY_VALUE,
|
---|
719 | character,
|
---|
720 | [[position.line, position.column, position.source]]
|
---|
721 | ]);
|
---|
722 |
|
---|
723 | buffer = [];
|
---|
724 | isBufferEmpty = true;
|
---|
725 | } else if (character == Marker.COMMA && level == Level.RULE && seekingValue) {
|
---|
726 | // comma within a property after space, e.g. a{background:url(image.png) ,<--
|
---|
727 | propertyToken.push([
|
---|
728 | Token.PROPERTY_VALUE,
|
---|
729 | character,
|
---|
730 | [[position.line, position.column, position.source]]
|
---|
731 | ]);
|
---|
732 |
|
---|
733 | buffer = [];
|
---|
734 | isBufferEmpty = true;
|
---|
735 | } else if (character == Marker.CLOSE_SQUARE_BRACKET
|
---|
736 | && propertyToken
|
---|
737 | && propertyToken.length > 1
|
---|
738 | && !isBufferEmpty
|
---|
739 | && isRepeatToken(buffer)) {
|
---|
740 | buffer.push(character);
|
---|
741 | serializedBuffer = buffer.join('').trim();
|
---|
742 | propertyToken[propertyToken.length - 1][1] += serializedBuffer;
|
---|
743 |
|
---|
744 | buffer = [];
|
---|
745 | isBufferEmpty = true;
|
---|
746 | } else if ((isSpace || (isNewLineNix && !isNewLineWin))
|
---|
747 | && level == Level.RULE
|
---|
748 | && seekingValue
|
---|
749 | && propertyToken
|
---|
750 | && !isBufferEmpty) {
|
---|
751 | // space or *nix newline within property, e.g. a{margin:0 <--
|
---|
752 | serializedBuffer = buffer.join('').trim();
|
---|
753 | propertyToken.push([
|
---|
754 | Token.PROPERTY_VALUE,
|
---|
755 | serializedBuffer,
|
---|
756 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
757 | ]);
|
---|
758 |
|
---|
759 | buffer = [];
|
---|
760 | isBufferEmpty = true;
|
---|
761 | } else if (isNewLineWin && level == Level.RULE && seekingValue && propertyToken && buffer.length > 1) {
|
---|
762 | // win newline within property, e.g. a{margin:0\r\n<--
|
---|
763 | serializedBuffer = buffer.join('').trim();
|
---|
764 | propertyToken.push([
|
---|
765 | Token.PROPERTY_VALUE,
|
---|
766 | serializedBuffer,
|
---|
767 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
768 | ]);
|
---|
769 |
|
---|
770 | buffer = [];
|
---|
771 | isBufferEmpty = true;
|
---|
772 | } else if (isNewLineWin && level == Level.RULE && seekingValue) {
|
---|
773 | // win newline
|
---|
774 | buffer = [];
|
---|
775 | isBufferEmpty = true;
|
---|
776 | } else if (isNewLineWin && buffer.length == 1) {
|
---|
777 | // ignore windows newline which is composed of two characters
|
---|
778 | buffer.pop();
|
---|
779 | isBufferEmpty = buffer.length === 0;
|
---|
780 | } else if (!isBufferEmpty || !isSpace && !isNewLineNix && !isNewLineWin && !isCarriageReturn) {
|
---|
781 | // any character
|
---|
782 | buffer.push(character);
|
---|
783 | isBufferEmpty = false;
|
---|
784 | }
|
---|
785 |
|
---|
786 | wasEscaped = isEscaped;
|
---|
787 | isEscaped = !wasEscaped && character == Marker.BACK_SLASH;
|
---|
788 | wasCommentStart = isCommentStart;
|
---|
789 | wasCommentEnd = isCommentEnd;
|
---|
790 |
|
---|
791 | position.line = (isNewLineWin || isNewLineNix || isCarriageReturn) ? position.line + 1 : position.line;
|
---|
792 | position.column = (isNewLineWin || isNewLineNix || isCarriageReturn) ? 0 : position.column + 1;
|
---|
793 | }
|
---|
794 |
|
---|
795 | if (seekingValue) {
|
---|
796 | externalContext.warnings.push('Missing \'}\' at ' + formatPosition([position.line, position.column, position.source]) + '.');
|
---|
797 | }
|
---|
798 |
|
---|
799 | if (seekingValue && buffer.length > 0) {
|
---|
800 | serializedBuffer = buffer.join('').trimRight().replace(TAIL_BROKEN_VALUE_PATTERN, '$1').trimRight();
|
---|
801 | propertyToken.push([
|
---|
802 | Token.PROPERTY_VALUE,
|
---|
803 | serializedBuffer,
|
---|
804 | [originalMetadata(metadata, serializedBuffer, externalContext)]
|
---|
805 | ]);
|
---|
806 |
|
---|
807 | buffer = [];
|
---|
808 | }
|
---|
809 |
|
---|
810 | if (buffer.length > 0) {
|
---|
811 | externalContext.warnings.push('Invalid character(s) \'' + buffer.join('') + '\' at ' + formatPosition(metadata) + '. Ignoring.');
|
---|
812 | }
|
---|
813 |
|
---|
814 | return allTokens;
|
---|
815 | }
|
---|
816 |
|
---|
817 | function isIgnoreStartComment(buffer) {
|
---|
818 | return IGNORE_START_COMMENT_PATTERN.test(buffer.join('') + Marker.FORWARD_SLASH);
|
---|
819 | }
|
---|
820 |
|
---|
821 | function isIgnoreEndComment(buffer) {
|
---|
822 | return IGNORE_END_COMMENT_PATTERN.test(buffer.join('') + Marker.FORWARD_SLASH);
|
---|
823 | }
|
---|
824 |
|
---|
825 | function originalMetadata(metadata, value, externalContext, selectorFallbacks) {
|
---|
826 | var source = metadata[2];
|
---|
827 |
|
---|
828 | return externalContext.inputSourceMapTracker.isTracking(source)
|
---|
829 | ? externalContext.inputSourceMapTracker.originalPositionFor(metadata, value.length, selectorFallbacks)
|
---|
830 | : metadata;
|
---|
831 | }
|
---|
832 |
|
---|
833 | function tokenTypeFrom(buffer) {
|
---|
834 | var isAtRule = buffer[0] == Marker.AT || buffer[0] == Marker.UNDERSCORE;
|
---|
835 | var ruleWord = buffer.join('').split(RULE_WORD_SEPARATOR_PATTERN)[0];
|
---|
836 |
|
---|
837 | if (isAtRule && BLOCK_RULES.indexOf(ruleWord) > -1) {
|
---|
838 | return Token.NESTED_BLOCK;
|
---|
839 | } if (isAtRule && AT_RULES.indexOf(ruleWord) > -1) {
|
---|
840 | return Token.AT_RULE;
|
---|
841 | } if (isAtRule) {
|
---|
842 | return Token.AT_RULE_BLOCK;
|
---|
843 | }
|
---|
844 | return Token.RULE;
|
---|
845 | }
|
---|
846 |
|
---|
847 | function tokenScopeFrom(tokenType) {
|
---|
848 | if (tokenType == Token.RULE) {
|
---|
849 | return Token.RULE_SCOPE;
|
---|
850 | } if (tokenType == Token.NESTED_BLOCK) {
|
---|
851 | return Token.NESTED_BLOCK_SCOPE;
|
---|
852 | } if (tokenType == Token.AT_RULE_BLOCK) {
|
---|
853 | return Token.AT_RULE_BLOCK_SCOPE;
|
---|
854 | }
|
---|
855 | }
|
---|
856 |
|
---|
857 | function isPageMarginBox(buffer) {
|
---|
858 | var serializedBuffer = buffer.join('').trim();
|
---|
859 |
|
---|
860 | return PAGE_MARGIN_BOXES.indexOf(serializedBuffer) > -1 || EXTRA_PAGE_BOXES.indexOf(serializedBuffer) > -1;
|
---|
861 | }
|
---|
862 |
|
---|
863 | function isRepeatToken(buffer) {
|
---|
864 | return REPEAT_PATTERN.test(buffer.join('') + Marker.CLOSE_SQUARE_BRACKET);
|
---|
865 | }
|
---|
866 |
|
---|
867 | module.exports = tokenize;
|
---|