| 1 | 'use strict';
|
|---|
| 2 | const browserslist = require('browserslist');
|
|---|
| 3 | const valueParser = require('postcss-value-parser');
|
|---|
| 4 | const { getArguments } = require('cssnano-utils');
|
|---|
| 5 |
|
|---|
| 6 | /**
|
|---|
| 7 | * Return the greatest common divisor
|
|---|
| 8 | * of two numbers.
|
|---|
| 9 | *
|
|---|
| 10 | * @param {number} a
|
|---|
| 11 | * @param {number} b
|
|---|
| 12 | * @return {number}
|
|---|
| 13 | */
|
|---|
| 14 | function gcd(a, b) {
|
|---|
| 15 | return b ? gcd(b, a % b) : a;
|
|---|
| 16 | }
|
|---|
| 17 |
|
|---|
| 18 | /**
|
|---|
| 19 | * @param {number} a
|
|---|
| 20 | * @param {number} b
|
|---|
| 21 | * @return {[number, number]}
|
|---|
| 22 | */
|
|---|
| 23 | function aspectRatio(a, b) {
|
|---|
| 24 | const divisor = gcd(a, b);
|
|---|
| 25 |
|
|---|
| 26 | return [a / divisor, b / divisor];
|
|---|
| 27 | }
|
|---|
| 28 |
|
|---|
| 29 | /**
|
|---|
| 30 | * @param {valueParser.Node[]} args
|
|---|
| 31 | * @return {string}
|
|---|
| 32 | */
|
|---|
| 33 | function split(args) {
|
|---|
| 34 | return args.map((arg) => valueParser.stringify(arg)).join('');
|
|---|
| 35 | }
|
|---|
| 36 |
|
|---|
| 37 | /**
|
|---|
| 38 | * @param {valueParser.Node} node
|
|---|
| 39 | * @return {void}
|
|---|
| 40 | */
|
|---|
| 41 | function removeNode(node) {
|
|---|
| 42 | node.value = '';
|
|---|
| 43 | node.type = 'word';
|
|---|
| 44 | }
|
|---|
| 45 |
|
|---|
| 46 | /**
|
|---|
| 47 | * @param {unknown[]} items
|
|---|
| 48 | * @return {string}
|
|---|
| 49 | */
|
|---|
| 50 | function sortAndDedupe(items) {
|
|---|
| 51 | const a = [...new Set(items)];
|
|---|
| 52 | a.sort();
|
|---|
| 53 | return a.join();
|
|---|
| 54 | }
|
|---|
| 55 |
|
|---|
| 56 | /**
|
|---|
| 57 | * @param {boolean} legacy
|
|---|
| 58 | * @param {import('postcss').AtRule} rule
|
|---|
| 59 | * @return {void}
|
|---|
| 60 | */
|
|---|
| 61 | function transform(legacy, rule) {
|
|---|
| 62 | const ruleName = rule.name.toLowerCase();
|
|---|
| 63 |
|
|---|
| 64 | // We should re-arrange parameters only for `@media` and `@supports` at-rules
|
|---|
| 65 | if (!rule.params || !['media', 'supports'].includes(ruleName)) {
|
|---|
| 66 | return;
|
|---|
| 67 | }
|
|---|
| 68 |
|
|---|
| 69 | const params = valueParser(rule.params);
|
|---|
| 70 |
|
|---|
| 71 | params.walk((node, index) => {
|
|---|
| 72 | if (node.type === 'div') {
|
|---|
| 73 | node.before = node.after = '';
|
|---|
| 74 | } else if (node.type === 'function') {
|
|---|
| 75 | node.before = '';
|
|---|
| 76 | if (
|
|---|
| 77 | node.nodes[0] &&
|
|---|
| 78 | node.nodes[0].type === 'word' &&
|
|---|
| 79 | node.nodes[0].value.startsWith('--') &&
|
|---|
| 80 | node.nodes[2] === undefined
|
|---|
| 81 | ) {
|
|---|
| 82 | node.after = ' ';
|
|---|
| 83 | } else {
|
|---|
| 84 | node.after = '';
|
|---|
| 85 | }
|
|---|
| 86 | if (
|
|---|
| 87 | node.nodes[4] &&
|
|---|
| 88 | node.nodes[0].value.toLowerCase().indexOf('-aspect-ratio') === 3
|
|---|
| 89 | ) {
|
|---|
| 90 | const [a, b] = aspectRatio(
|
|---|
| 91 | Number(node.nodes[2].value),
|
|---|
| 92 | Number(node.nodes[4].value)
|
|---|
| 93 | );
|
|---|
| 94 |
|
|---|
| 95 | node.nodes[2].value = a.toString();
|
|---|
| 96 | node.nodes[4].value = b.toString();
|
|---|
| 97 | }
|
|---|
| 98 | } else if (node.type === 'space') {
|
|---|
| 99 | node.value = ' ';
|
|---|
| 100 | } else {
|
|---|
| 101 | const prevWord = params.nodes[index - 2];
|
|---|
| 102 |
|
|---|
| 103 | if (
|
|---|
| 104 | node.value.toLowerCase() === 'all' &&
|
|---|
| 105 | rule.name.toLowerCase() === 'media' &&
|
|---|
| 106 | !prevWord
|
|---|
| 107 | ) {
|
|---|
| 108 | const nextWord = params.nodes[index + 2];
|
|---|
| 109 |
|
|---|
| 110 | if (!legacy || nextWord) {
|
|---|
| 111 | removeNode(node);
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | if (nextWord && nextWord.value.toLowerCase() === 'and') {
|
|---|
| 115 | const nextSpace = params.nodes[index + 1];
|
|---|
| 116 | const secondSpace = params.nodes[index + 3];
|
|---|
| 117 |
|
|---|
| 118 | removeNode(nextWord);
|
|---|
| 119 | removeNode(nextSpace);
|
|---|
| 120 | removeNode(secondSpace);
|
|---|
| 121 | }
|
|---|
| 122 | }
|
|---|
| 123 | }
|
|---|
| 124 | }, true);
|
|---|
| 125 |
|
|---|
| 126 | rule.params = sortAndDedupe(getArguments(params).map(split));
|
|---|
| 127 |
|
|---|
| 128 | if (!rule.params.length) {
|
|---|
| 129 | rule.raws.afterName = '';
|
|---|
| 130 | }
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | const allBugBrowers = new Set(['ie 10', 'ie 11']);
|
|---|
| 134 |
|
|---|
| 135 | /**
|
|---|
| 136 | * @type {import('postcss').PluginCreator<browserslist.Options>}
|
|---|
| 137 | * @param {browserslist.Options} options
|
|---|
| 138 | * @return {import('postcss').Plugin}
|
|---|
| 139 | */
|
|---|
| 140 | function pluginCreator(options = {}) {
|
|---|
| 141 | const browsers = browserslist(null, {
|
|---|
| 142 | stats: options.stats,
|
|---|
| 143 | path: __dirname,
|
|---|
| 144 | env: options.env,
|
|---|
| 145 | });
|
|---|
| 146 |
|
|---|
| 147 | const hasAllBug = browsers.some((browser) => allBugBrowers.has(browser));
|
|---|
| 148 | return {
|
|---|
| 149 | postcssPlugin: 'postcss-minify-params',
|
|---|
| 150 |
|
|---|
| 151 | OnceExit(css) {
|
|---|
| 152 | css.walkAtRules((rule) => transform(hasAllBug, rule));
|
|---|
| 153 | },
|
|---|
| 154 | };
|
|---|
| 155 | }
|
|---|
| 156 |
|
|---|
| 157 | pluginCreator.postcss = true;
|
|---|
| 158 | module.exports = pluginCreator;
|
|---|