| "use strict"; |
| |
| Object.defineProperty(exports, "__esModule", { |
| value: true |
| }); |
| |
| var _postcss = require("postcss"); |
| |
| var _alphanumSort = require("alphanum-sort"); |
| |
| var _alphanumSort2 = _interopRequireDefault(_alphanumSort); |
| |
| var _has = require("has"); |
| |
| var _has2 = _interopRequireDefault(_has); |
| |
| var _postcssSelectorParser = require("postcss-selector-parser"); |
| |
| var _postcssSelectorParser2 = _interopRequireDefault(_postcssSelectorParser); |
| |
| var _unquote = require("./lib/unquote"); |
| |
| var _unquote2 = _interopRequireDefault(_unquote); |
| |
| var _canUnquote = require("./lib/canUnquote"); |
| |
| var _canUnquote2 = _interopRequireDefault(_canUnquote); |
| |
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
| |
| const pseudoElements = ["::before", "::after", "::first-letter", "::first-line"]; |
| |
| function getParsed(selectors, callback) { |
| return (0, _postcssSelectorParser2.default)(callback).processSync(selectors); |
| } |
| |
| function attribute(selector) { |
| if (selector.value) { |
| // Join selectors that are split over new lines |
| selector.value = selector.value.replace(/\\\n/g, "").trim(); |
| |
| if ((0, _canUnquote2.default)(selector.value)) { |
| selector.value = (0, _unquote2.default)(selector.value); |
| } |
| |
| selector.operator = selector.operator.trim(); |
| } |
| |
| if (!selector.raws) { |
| selector.raws = {}; |
| } |
| |
| if (!selector.raws.spaces) { |
| selector.raws.spaces = {}; |
| } |
| |
| selector.raws.spaces.attribute = { |
| before: "", |
| after: "" |
| }; |
| |
| selector.raws.spaces.operator = { |
| before: "", |
| after: "" |
| }; |
| |
| selector.raws.spaces.value = { |
| before: "", |
| after: selector.insensitive ? " " : "" |
| }; |
| |
| if (selector.insensitive) { |
| selector.raws.spaces.insensitive = { |
| before: "", |
| after: "" |
| }; |
| } |
| |
| selector.attribute = selector.attribute.trim(); |
| } |
| |
| function combinator(selector) { |
| const value = selector.value.trim(); |
| |
| selector.value = value.length ? value : " "; |
| } |
| |
| const pseudoReplacements = { |
| ":nth-child": ":first-child", |
| ":nth-of-type": ":first-of-type", |
| ":nth-last-child": ":last-child", |
| ":nth-last-of-type": ":last-of-type" |
| }; |
| |
| function pseudo(selector) { |
| const value = selector.value.toLowerCase(); |
| |
| if (selector.nodes.length === 1 && pseudoReplacements[value]) { |
| const first = selector.at(0); |
| const one = first.at(0); |
| |
| if (first.length === 1) { |
| if (one.value === "1") { |
| selector.replaceWith(_postcssSelectorParser2.default.pseudo({ |
| value: pseudoReplacements[value] |
| })); |
| } |
| |
| if (one.value.toLowerCase() === "even") { |
| one.value = "2n"; |
| } |
| } |
| |
| if (first.length === 3) { |
| const two = first.at(1); |
| const three = first.at(2); |
| |
| if (one.value.toLowerCase() === "2n" && two.value === "+" && three.value === "1") { |
| one.value = "odd"; |
| |
| two.remove(); |
| three.remove(); |
| } |
| } |
| |
| return; |
| } |
| |
| const uniques = []; |
| |
| selector.walk(child => { |
| if (child.type === "selector") { |
| const childStr = String(child); |
| |
| if (!~uniques.indexOf(childStr)) { |
| uniques.push(childStr); |
| } else { |
| child.remove(); |
| } |
| } |
| }); |
| |
| if (~pseudoElements.indexOf(value)) { |
| selector.value = selector.value.slice(1); |
| } |
| } |
| |
| const tagReplacements = { |
| from: "0%", |
| "100%": "to" |
| }; |
| |
| function tag(selector) { |
| const value = selector.value.toLowerCase(); |
| |
| if ((0, _has2.default)(tagReplacements, value)) { |
| selector.value = tagReplacements[value]; |
| } |
| } |
| |
| function universal(selector) { |
| const next = selector.next(); |
| |
| if (next && next.type !== "combinator") { |
| selector.remove(); |
| } |
| } |
| |
| const reducers = { |
| attribute, |
| combinator, |
| pseudo, |
| tag, |
| universal |
| }; |
| |
| exports.default = (0, _postcss.plugin)("postcss-minify-selectors", () => { |
| return css => { |
| const cache = {}; |
| |
| css.walkRules(rule => { |
| const selector = rule.raws.selector && rule.raws.selector.value === rule.selector ? rule.raws.selector.raw : rule.selector; |
| |
| // If the selector ends with a ':' it is likely a part of a custom mixin, |
| // so just pass through. |
| if (selector[selector.length - 1] === ":") { |
| return; |
| } |
| |
| if (cache[selector]) { |
| rule.selector = cache[selector]; |
| |
| return; |
| } |
| |
| const optimizedSelector = getParsed(selector, selectors => { |
| selectors.nodes = (0, _alphanumSort2.default)(selectors.nodes, { insensitive: true }); |
| |
| const uniqueSelectors = []; |
| |
| selectors.walk(sel => { |
| const { type } = sel; |
| |
| // Trim whitespace around the value |
| sel.spaces.before = sel.spaces.after = ""; |
| |
| if ((0, _has2.default)(reducers, type)) { |
| reducers[type](sel); |
| |
| return; |
| } |
| |
| const toString = String(sel); |
| |
| if (type === "selector" && sel.parent.type !== "pseudo") { |
| if (!~uniqueSelectors.indexOf(toString)) { |
| uniqueSelectors.push(toString); |
| } else { |
| sel.remove(); |
| } |
| } |
| }); |
| }); |
| |
| rule.selector = optimizedSelector; |
| cache[selector] = optimizedSelector; |
| }); |
| }; |
| }); |
| module.exports = exports["default"]; |