UNPKG

3.58 kBJavaScriptView Raw
1'use strict';
2const browserslist = require('browserslist');
3const valueParser = require('postcss-value-parser');
4const { 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 */
14function 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 */
23function 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 */
33function split(args) {
34 return args.map((arg) => valueParser.stringify(arg)).join('');
35}
36
37/**
38 * @param {valueParser.Node} node
39 * @return {void}
40 */
41function removeNode(node) {
42 node.value = '';
43 node.type = 'word';
44}
45
46/**
47 * @param {unknown[]} items
48 * @return {string}
49 */
50function 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 */
61function 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
133const 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 */
140function 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
157pluginCreator.postcss = true;
158module.exports = pluginCreator;