UNPKG

23.7 kBJavaScriptView Raw
1"use strict";
2
3var parser = require('postcss-value-parser');
4
5var Value = require('./value');
6
7var insertAreas = require('./hacks/grid-utils').insertAreas;
8
9var OLD_LINEAR = /(^|[^-])linear-gradient\(\s*(top|left|right|bottom)/i;
10var OLD_RADIAL = /(^|[^-])radial-gradient\(\s*\d+(\w*|%)\s+\d+(\w*|%)\s*,/i;
11var IGNORE_NEXT = /(!\s*)?autoprefixer:\s*ignore\s+next/i;
12var GRID_REGEX = /(!\s*)?autoprefixer\s*grid:\s*(on|off|(no-)?autoplace)/i;
13var SIZES = ['width', 'height', 'min-width', 'max-width', 'min-height', 'max-height', 'inline-size', 'min-inline-size', 'max-inline-size', 'block-size', 'min-block-size', 'max-block-size'];
14
15function hasGridTemplate(decl) {
16 return decl.parent.some(function (i) {
17 return i.prop === 'grid-template' || i.prop === 'grid-template-areas';
18 });
19}
20
21function hasRowsAndColumns(decl) {
22 var hasRows = decl.parent.some(function (i) {
23 return i.prop === 'grid-template-rows';
24 });
25 var hasColumns = decl.parent.some(function (i) {
26 return i.prop === 'grid-template-columns';
27 });
28 return hasRows && hasColumns;
29}
30
31var Processor =
32/*#__PURE__*/
33function () {
34 function Processor(prefixes) {
35 this.prefixes = prefixes;
36 }
37 /**
38 * Add necessary prefixes
39 */
40
41
42 var _proto = Processor.prototype;
43
44 _proto.add = function add(css, result) {
45 var _this = this;
46
47 // At-rules
48 var resolution = this.prefixes.add['@resolution'];
49 var keyframes = this.prefixes.add['@keyframes'];
50 var viewport = this.prefixes.add['@viewport'];
51 var supports = this.prefixes.add['@supports'];
52 css.walkAtRules(function (rule) {
53 if (rule.name === 'keyframes') {
54 if (!_this.disabled(rule, result)) {
55 return keyframes && keyframes.process(rule);
56 }
57 } else if (rule.name === 'viewport') {
58 if (!_this.disabled(rule, result)) {
59 return viewport && viewport.process(rule);
60 }
61 } else if (rule.name === 'supports') {
62 if (_this.prefixes.options.supports !== false && !_this.disabled(rule, result)) {
63 return supports.process(rule);
64 }
65 } else if (rule.name === 'media' && rule.params.includes('-resolution')) {
66 if (!_this.disabled(rule, result)) {
67 return resolution && resolution.process(rule);
68 }
69 }
70
71 return undefined;
72 }); // Selectors
73
74 css.walkRules(function (rule) {
75 if (_this.disabled(rule, result)) return undefined;
76 return _this.prefixes.add.selectors.map(function (selector) {
77 return selector.process(rule, result);
78 });
79 });
80
81 function insideGrid(decl) {
82 return decl.parent.nodes.some(function (node) {
83 if (node.type !== 'decl') return false;
84 var displayGrid = node.prop === 'display' && /(inline-)?grid/.test(node.value);
85 var gridTemplate = node.prop.startsWith('grid-template');
86 var gridGap = /^grid-([A-z]+-)?gap/.test(node.prop);
87 return displayGrid || gridTemplate || gridGap;
88 });
89 }
90
91 function insideFlex(decl) {
92 return decl.parent.some(function (node) {
93 return node.prop === 'display' && /(inline-)?flex/.test(node.value);
94 });
95 }
96
97 var gridPrefixes = this.gridStatus(css, result) && this.prefixes.add['grid-area'] && this.prefixes.add['grid-area'].prefixes;
98 css.walkDecls(function (decl) {
99 if (_this.disabledDecl(decl, result)) return undefined;
100 var parent = decl.parent;
101 var prop = decl.prop;
102 var value = decl.value;
103
104 if (prop === 'grid-row-span') {
105 result.warn('grid-row-span is not part of final Grid Layout. Use grid-row.', {
106 node: decl
107 });
108 return undefined;
109 } else if (prop === 'grid-column-span') {
110 result.warn('grid-column-span is not part of final Grid Layout. Use grid-column.', {
111 node: decl
112 });
113 return undefined;
114 } else if (prop === 'display' && value === 'box') {
115 result.warn('You should write display: flex by final spec ' + 'instead of display: box', {
116 node: decl
117 });
118 return undefined;
119 } else if (prop === 'text-emphasis-position') {
120 if (value === 'under' || value === 'over') {
121 result.warn('You should use 2 values for text-emphasis-position ' + 'For example, `under left` instead of just `under`.', {
122 node: decl
123 });
124 }
125 } else if (/^(align|justify|place)-(items|content)$/.test(prop) && insideFlex(decl)) {
126 if (value === 'start' || value === 'end') {
127 result.warn(value + " value has mixed support, consider using " + ("flex-" + value + " instead"), {
128 node: decl
129 });
130 }
131 } else if (prop === 'text-decoration-skip' && value === 'ink') {
132 result.warn('Replace text-decoration-skip: ink to ' + 'text-decoration-skip-ink: auto, because spec had been changed', {
133 node: decl
134 });
135 } else {
136 if (gridPrefixes) {
137 if (/^(align|justify|place)-items$/.test(prop) && insideGrid(decl)) {
138 var fixed = prop.replace('-items', '-self');
139 result.warn("IE does not support " + prop + " on grid containers. " + ("Try using " + fixed + " on child elements instead: ") + (decl.parent.selector + " > * { " + fixed + ": " + decl.value + " }"), {
140 node: decl
141 });
142 } else if (/^(align|justify|place)-content$/.test(prop) && insideGrid(decl)) {
143 result.warn("IE does not support " + decl.prop + " on grid containers", {
144 node: decl
145 });
146 } else if (prop === 'display' && decl.value === 'contents') {
147 result.warn('Please do not use display: contents; ' + 'if you have grid setting enabled', {
148 node: decl
149 });
150 return undefined;
151 } else if (decl.prop === 'grid-gap') {
152 var status = _this.gridStatus(decl, result);
153
154 if (status === 'autoplace' && !hasRowsAndColumns(decl) && !hasGridTemplate(decl)) {
155 result.warn('grid-gap only works if grid-template(-areas) is being ' + 'used or both rows and columns have been declared ' + 'and cells have not been manually ' + 'placed inside the explicit grid', {
156 node: decl
157 });
158 } else if ((status === true || status === 'no-autoplace') && !hasGridTemplate(decl)) {
159 result.warn('grid-gap only works if grid-template(-areas) is being used', {
160 node: decl
161 });
162 }
163 } else if (prop === 'grid-auto-columns') {
164 result.warn('grid-auto-columns is not supported by IE', {
165 node: decl
166 });
167 return undefined;
168 } else if (prop === 'grid-auto-rows') {
169 result.warn('grid-auto-rows is not supported by IE', {
170 node: decl
171 });
172 return undefined;
173 } else if (prop === 'grid-auto-flow') {
174 var hasRows = parent.some(function (i) {
175 return i.prop === 'grid-template-rows';
176 });
177 var hasCols = parent.some(function (i) {
178 return i.prop === 'grid-template-columns';
179 });
180
181 if (hasGridTemplate(decl)) {
182 result.warn('grid-auto-flow is not supported by IE', {
183 node: decl
184 });
185 } else if (value.includes('dense')) {
186 result.warn('grid-auto-flow: dense is not supported by IE', {
187 node: decl
188 });
189 } else if (!hasRows && !hasCols) {
190 result.warn('grid-auto-flow works only if grid-template-rows and ' + 'grid-template-columns are present in the same rule', {
191 node: decl
192 });
193 }
194
195 return undefined;
196 } else if (value.includes('auto-fit')) {
197 result.warn('auto-fit value is not supported by IE', {
198 node: decl,
199 word: 'auto-fit'
200 });
201 return undefined;
202 } else if (value.includes('auto-fill')) {
203 result.warn('auto-fill value is not supported by IE', {
204 node: decl,
205 word: 'auto-fill'
206 });
207 return undefined;
208 } else if (prop.startsWith('grid-template') && value.includes('[')) {
209 result.warn('Autoprefixer currently does not support line names. ' + 'Try using grid-template-areas instead.', {
210 node: decl,
211 word: '['
212 });
213 }
214 }
215
216 if (value.includes('radial-gradient')) {
217 if (OLD_RADIAL.test(decl.value)) {
218 result.warn('Gradient has outdated direction syntax. ' + 'New syntax is like `closest-side at 0 0` ' + 'instead of `0 0, closest-side`.', {
219 node: decl
220 });
221 } else {
222 var ast = parser(value);
223
224 for (var _iterator = ast.nodes, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
225 var _ref;
226
227 if (_isArray) {
228 if (_i >= _iterator.length) break;
229 _ref = _iterator[_i++];
230 } else {
231 _i = _iterator.next();
232 if (_i.done) break;
233 _ref = _i.value;
234 }
235
236 var i = _ref;
237
238 if (i.type === 'function' && i.value === 'radial-gradient') {
239 for (var _iterator2 = i.nodes, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
240 var _ref2;
241
242 if (_isArray2) {
243 if (_i2 >= _iterator2.length) break;
244 _ref2 = _iterator2[_i2++];
245 } else {
246 _i2 = _iterator2.next();
247 if (_i2.done) break;
248 _ref2 = _i2.value;
249 }
250
251 var word = _ref2;
252
253 if (word.type === 'word') {
254 if (word.value === 'cover') {
255 result.warn('Gradient has outdated direction syntax. ' + 'Replace `cover` to `farthest-corner`.', {
256 node: decl
257 });
258 } else if (word.value === 'contain') {
259 result.warn('Gradient has outdated direction syntax. ' + 'Replace `contain` to `closest-side`.', {
260 node: decl
261 });
262 }
263 }
264 }
265 }
266 }
267 }
268 }
269
270 if (value.includes('linear-gradient')) {
271 if (OLD_LINEAR.test(value)) {
272 result.warn('Gradient has outdated direction syntax. ' + 'New syntax is like `to left` instead of `right`.', {
273 node: decl
274 });
275 }
276 }
277 }
278
279 if (SIZES.includes(decl.prop)) {
280 if (!decl.value.includes('-fill-available')) {
281 if (decl.value.includes('fill-available')) {
282 result.warn('Replace fill-available to stretch, ' + 'because spec had been changed', {
283 node: decl
284 });
285 } else if (decl.value.includes('fill')) {
286 var _ast = parser(value);
287
288 if (_ast.nodes.some(function (i) {
289 return i.type === 'word' && i.value === 'fill';
290 })) {
291 result.warn('Replace fill to stretch, because spec had been changed', {
292 node: decl
293 });
294 }
295 }
296 }
297 }
298
299 var prefixer;
300
301 if (decl.prop === 'transition' || decl.prop === 'transition-property') {
302 // Transition
303 return _this.prefixes.transition.add(decl, result);
304 } else if (decl.prop === 'align-self') {
305 // align-self flexbox or grid
306 var display = _this.displayType(decl);
307
308 if (display !== 'grid' && _this.prefixes.options.flexbox !== false) {
309 prefixer = _this.prefixes.add['align-self'];
310
311 if (prefixer && prefixer.prefixes) {
312 prefixer.process(decl);
313 }
314 }
315
316 if (display !== 'flex' && _this.gridStatus(decl, result) !== false) {
317 prefixer = _this.prefixes.add['grid-row-align'];
318
319 if (prefixer && prefixer.prefixes) {
320 return prefixer.process(decl, result);
321 }
322 }
323 } else if (decl.prop === 'justify-self') {
324 // justify-self flexbox or grid
325 var _display = _this.displayType(decl);
326
327 if (_display !== 'flex' && _this.gridStatus(decl, result) !== false) {
328 prefixer = _this.prefixes.add['grid-column-align'];
329
330 if (prefixer && prefixer.prefixes) {
331 return prefixer.process(decl, result);
332 }
333 }
334 } else if (decl.prop === 'place-self') {
335 prefixer = _this.prefixes.add['place-self'];
336
337 if (prefixer && prefixer.prefixes && _this.gridStatus(decl, result) !== false) {
338 return prefixer.process(decl, result);
339 }
340 } else {
341 // Properties
342 prefixer = _this.prefixes.add[decl.prop];
343
344 if (prefixer && prefixer.prefixes) {
345 return prefixer.process(decl, result);
346 }
347 }
348
349 return undefined;
350 }); // Insert grid-area prefixes. We need to be able to store the different
351 // rules as a data and hack API is not enough for this
352
353 if (this.gridStatus(css, result)) {
354 insertAreas(css, this.disabled);
355 } // Values
356
357
358 return css.walkDecls(function (decl) {
359 if (_this.disabledValue(decl, result)) return;
360
361 var unprefixed = _this.prefixes.unprefixed(decl.prop);
362
363 for (var _iterator3 = _this.prefixes.values('add', unprefixed), _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
364 var _ref3;
365
366 if (_isArray3) {
367 if (_i3 >= _iterator3.length) break;
368 _ref3 = _iterator3[_i3++];
369 } else {
370 _i3 = _iterator3.next();
371 if (_i3.done) break;
372 _ref3 = _i3.value;
373 }
374
375 var value = _ref3;
376 value.process(decl, result);
377 }
378
379 Value.save(_this.prefixes, decl);
380 });
381 }
382 /**
383 * Remove unnecessary pefixes
384 */
385 ;
386
387 _proto.remove = function remove(css, result) {
388 var _this2 = this;
389
390 // At-rules
391 var resolution = this.prefixes.remove['@resolution'];
392 css.walkAtRules(function (rule, i) {
393 if (_this2.prefixes.remove["@" + rule.name]) {
394 if (!_this2.disabled(rule, result)) {
395 rule.parent.removeChild(i);
396 }
397 } else if (rule.name === 'media' && rule.params.includes('-resolution') && resolution) {
398 resolution.clean(rule);
399 }
400 }); // Selectors
401
402 var _loop = function _loop() {
403 if (_isArray4) {
404 if (_i4 >= _iterator4.length) return "break";
405 _ref4 = _iterator4[_i4++];
406 } else {
407 _i4 = _iterator4.next();
408 if (_i4.done) return "break";
409 _ref4 = _i4.value;
410 }
411
412 var checker = _ref4;
413 css.walkRules(function (rule, i) {
414 if (checker.check(rule)) {
415 if (!_this2.disabled(rule, result)) {
416 rule.parent.removeChild(i);
417 }
418 }
419 });
420 };
421
422 for (var _iterator4 = this.prefixes.remove.selectors, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
423 var _ref4;
424
425 var _ret = _loop();
426
427 if (_ret === "break") break;
428 }
429
430 return css.walkDecls(function (decl, i) {
431 if (_this2.disabled(decl, result)) return;
432 var rule = decl.parent;
433
434 var unprefixed = _this2.prefixes.unprefixed(decl.prop); // Transition
435
436
437 if (decl.prop === 'transition' || decl.prop === 'transition-property') {
438 _this2.prefixes.transition.remove(decl);
439 } // Properties
440
441
442 if (_this2.prefixes.remove[decl.prop] && _this2.prefixes.remove[decl.prop].remove) {
443 var notHack = _this2.prefixes.group(decl).down(function (other) {
444 return _this2.prefixes.normalize(other.prop) === unprefixed;
445 });
446
447 if (unprefixed === 'flex-flow') {
448 notHack = true;
449 }
450
451 if (decl.prop === '-webkit-box-orient') {
452 var hacks = {
453 'flex-direction': true,
454 'flex-flow': true
455 };
456 if (!decl.parent.some(function (j) {
457 return hacks[j.prop];
458 })) return;
459 }
460
461 if (notHack && !_this2.withHackValue(decl)) {
462 if (decl.raw('before').includes('\n')) {
463 _this2.reduceSpaces(decl);
464 }
465
466 rule.removeChild(i);
467 return;
468 }
469 } // Values
470
471
472 for (var _iterator5 = _this2.prefixes.values('remove', unprefixed), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) {
473 var _ref5;
474
475 if (_isArray5) {
476 if (_i5 >= _iterator5.length) break;
477 _ref5 = _iterator5[_i5++];
478 } else {
479 _i5 = _iterator5.next();
480 if (_i5.done) break;
481 _ref5 = _i5.value;
482 }
483
484 var checker = _ref5;
485
486 if (!checker.check(decl.value)) {
487 continue;
488 }
489
490 unprefixed = checker.unprefixed;
491
492 var _notHack = _this2.prefixes.group(decl).down(function (other) {
493 return other.value.includes(unprefixed);
494 });
495
496 if (_notHack) {
497 rule.removeChild(i);
498 return;
499 }
500 }
501 });
502 }
503 /**
504 * Some rare old values, which is not in standard
505 */
506 ;
507
508 _proto.withHackValue = function withHackValue(decl) {
509 return decl.prop === '-webkit-background-clip' && decl.value === 'text';
510 }
511 /**
512 * Check for grid/flexbox options.
513 */
514 ;
515
516 _proto.disabledValue = function disabledValue(node, result) {
517 if (this.gridStatus(node, result) === false && node.type === 'decl') {
518 if (node.prop === 'display' && node.value.includes('grid')) {
519 return true;
520 }
521 }
522
523 if (this.prefixes.options.flexbox === false && node.type === 'decl') {
524 if (node.prop === 'display' && node.value.includes('flex')) {
525 return true;
526 }
527 }
528
529 return this.disabled(node, result);
530 }
531 /**
532 * Check for grid/flexbox options.
533 */
534 ;
535
536 _proto.disabledDecl = function disabledDecl(node, result) {
537 if (this.gridStatus(node, result) === false && node.type === 'decl') {
538 if (node.prop.includes('grid') || node.prop === 'justify-items') {
539 return true;
540 }
541 }
542
543 if (this.prefixes.options.flexbox === false && node.type === 'decl') {
544 var other = ['order', 'justify-content', 'align-items', 'align-content'];
545
546 if (node.prop.includes('flex') || other.includes(node.prop)) {
547 return true;
548 }
549 }
550
551 return this.disabled(node, result);
552 }
553 /**
554 * Check for control comment and global options
555 */
556 ;
557
558 _proto.disabled = function disabled(node, result) {
559 if (!node) return false;
560
561 if (node._autoprefixerDisabled !== undefined) {
562 return node._autoprefixerDisabled;
563 }
564
565 if (node.parent) {
566 var p = node.prev();
567
568 if (p && p.type === 'comment' && IGNORE_NEXT.test(p.text)) {
569 node._autoprefixerDisabled = true;
570 node._autoprefixerSelfDisabled = true;
571 return true;
572 }
573 }
574
575 var value = null;
576
577 if (node.nodes) {
578 var status;
579 node.each(function (i) {
580 if (i.type !== 'comment') return;
581
582 if (/(!\s*)?autoprefixer:\s*(off|on)/i.test(i.text)) {
583 if (typeof status !== 'undefined') {
584 result.warn('Second Autoprefixer control comment ' + 'was ignored. Autoprefixer applies control ' + 'comment to whole block, not to next rules.', {
585 node: i
586 });
587 } else {
588 status = /on/i.test(i.text);
589 }
590 }
591 });
592
593 if (status !== undefined) {
594 value = !status;
595 }
596 }
597
598 if (!node.nodes || value === null) {
599 if (node.parent) {
600 var isParentDisabled = this.disabled(node.parent, result);
601
602 if (node.parent._autoprefixerSelfDisabled === true) {
603 value = false;
604 } else {
605 value = isParentDisabled;
606 }
607 } else {
608 value = false;
609 }
610 }
611
612 node._autoprefixerDisabled = value;
613 return value;
614 }
615 /**
616 * Normalize spaces in cascade declaration group
617 */
618 ;
619
620 _proto.reduceSpaces = function reduceSpaces(decl) {
621 var stop = false;
622 this.prefixes.group(decl).up(function () {
623 stop = true;
624 return true;
625 });
626
627 if (stop) {
628 return;
629 }
630
631 var parts = decl.raw('before').split('\n');
632 var prevMin = parts[parts.length - 1].length;
633 var diff = false;
634 this.prefixes.group(decl).down(function (other) {
635 parts = other.raw('before').split('\n');
636 var last = parts.length - 1;
637
638 if (parts[last].length > prevMin) {
639 if (diff === false) {
640 diff = parts[last].length - prevMin;
641 }
642
643 parts[last] = parts[last].slice(0, -diff);
644 other.raws.before = parts.join('\n');
645 }
646 });
647 }
648 /**
649 * Is it flebox or grid rule
650 */
651 ;
652
653 _proto.displayType = function displayType(decl) {
654 for (var _iterator6 = decl.parent.nodes, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) {
655 var _ref6;
656
657 if (_isArray6) {
658 if (_i6 >= _iterator6.length) break;
659 _ref6 = _iterator6[_i6++];
660 } else {
661 _i6 = _iterator6.next();
662 if (_i6.done) break;
663 _ref6 = _i6.value;
664 }
665
666 var i = _ref6;
667
668 if (i.prop !== 'display') {
669 continue;
670 }
671
672 if (i.value.includes('flex')) {
673 return 'flex';
674 }
675
676 if (i.value.includes('grid')) {
677 return 'grid';
678 }
679 }
680
681 return false;
682 }
683 /**
684 * Set grid option via control comment
685 */
686 ;
687
688 _proto.gridStatus = function gridStatus(node, result) {
689 if (!node) return false;
690
691 if (node._autoprefixerGridStatus !== undefined) {
692 return node._autoprefixerGridStatus;
693 }
694
695 var value = null;
696
697 if (node.nodes) {
698 var status;
699 node.each(function (i) {
700 if (i.type !== 'comment') return;
701
702 if (GRID_REGEX.test(i.text)) {
703 var hasAutoplace = /:\s*autoplace/i.test(i.text);
704 var noAutoplace = /no-autoplace/i.test(i.text);
705
706 if (typeof status !== 'undefined') {
707 result.warn('Second Autoprefixer grid control comment was ' + 'ignored. Autoprefixer applies control comments to the whole ' + 'block, not to the next rules.', {
708 node: i
709 });
710 } else if (hasAutoplace) {
711 status = 'autoplace';
712 } else if (noAutoplace) {
713 status = true;
714 } else {
715 status = /on/i.test(i.text);
716 }
717 }
718 });
719
720 if (status !== undefined) {
721 value = status;
722 }
723 }
724
725 if (node.type === 'atrule' && node.name === 'supports') {
726 var params = node.params;
727
728 if (params.includes('grid') && params.includes('auto')) {
729 value = false;
730 }
731 }
732
733 if (!node.nodes || value === null) {
734 if (node.parent) {
735 var isParentGrid = this.gridStatus(node.parent, result);
736
737 if (node.parent._autoprefixerSelfDisabled === true) {
738 value = false;
739 } else {
740 value = isParentGrid;
741 }
742 } else {
743 value = this.prefixes.options.grid;
744 }
745 }
746
747 node._autoprefixerGridStatus = value;
748 return value;
749 };
750
751 return Processor;
752}();
753
754module.exports = Processor;
\No newline at end of file