UNPKG

3.73 kBJavaScriptView Raw
1'use strict';
2
3var postcss = require('postcss');
4var sort = require('alphanum-sort');
5var unquote = require('./lib/unquote');
6var parser = require('postcss-selector-parser');
7
8var pseudoElements = ['::before', '::after', '::first-letter', '::first-line'];
9
10function getParsed (selectors, callback) {
11 return parser(callback).process(selectors).result;
12}
13
14/**
15 * Can unquote attribute detection from mothereff.in
16 * Copyright Mathias Bynens <https://mathiasbynens.be/>
17 * https://github.com/mathiasbynens/mothereff.in
18 */
19var escapes = /\\([0-9A-Fa-f]{1,6})[ \t\n\f\r]?/g;
20var range = /[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
21
22function canUnquote (value) {
23 value = unquote(value);
24 if (value) {
25 value = value.replace(escapes, 'a').replace(/\\./g, 'a');
26 return !(range.test(value) || /^(?:-?\d|--)/.test(value));
27 }
28 return false;
29}
30
31function optimise (rule) {
32 var selector = rule.raws.selector && rule.raws.selector.raw || rule.selector;
33 rule.selector = getParsed(selector, function (selectors) {
34 selectors.nodes = sort(selectors.nodes, {insensitive: true});
35 var uniqueSelectors = [];
36 selectors.eachInside(function (selector) {
37 var next = selector.next();
38 var toString = String(selector);
39 // Trim whitespace around the value
40 selector.spaces.before = selector.spaces.after = '';
41 if (selector.type === 'attribute') {
42 if (selector.value) {
43 // Join selectors that are split over new lines
44 selector.value = selector.value.replace(/\\\n/g, '').trim();
45 if (canUnquote(selector.value)) {
46 selector.value = unquote(selector.value);
47 }
48 selector.operator = selector.operator.trim();
49 }
50 if (selector.raw) { selector.raw.insensitive = ''; }
51 selector.attribute = selector.attribute.trim();
52 }
53 if (selector.type === 'combinator') {
54 var value = selector.value.trim();
55 selector.value = value.length ? value : ' ';
56 }
57 if (selector.type === 'pseudo') {
58 var uniques = [];
59 selector.eachInside(function (child) {
60 if (child.type === 'selector') {
61 if (!~uniques.indexOf(String(child))) {
62 uniques.push(String(child));
63 } else {
64 child.removeSelf();
65 }
66 }
67 });
68 if (~pseudoElements.indexOf(selector.value)) {
69 selector.value = selector.value.slice(1);
70 }
71 }
72 if (selector.type === 'selector' && selector.parent.type !== 'pseudo') {
73 if (!~uniqueSelectors.indexOf(toString)) {
74 uniqueSelectors.push(toString);
75 } else {
76 selector.removeSelf();
77 }
78 }
79 if (selector.type === 'tag') {
80 if (selector.value === 'from') { selector.value = '0%'; }
81 if (selector.value === '100%') { selector.value = 'to'; }
82 }
83 if (selector.type === 'universal') {
84 if (next && next.type !== 'combinator') {
85 selector.removeSelf();
86 }
87 }
88 });
89 });
90}
91
92module.exports = postcss.plugin('postcss-minify-selectors', function () {
93 return function (css) {
94 css.walkRules(function (rule) {
95 return optimise(rule);
96 });
97 };
98});