index.js 1.56 KB
'use strict';
const selectorParser = require('postcss-selector-parser');

/**
 * @param {string} selectors
 * @param {selectorParser.SyncProcessor<void>} callback
 * @return {string}
 */
function parseSelectors(selectors, callback) {
  return selectorParser(callback).processSync(selectors);
}

/**
 * @param {import('postcss').Rule} rule
 * @return {string}
 */
function unique(rule) {
  const selector = [...new Set(rule.selectors)];
  selector.sort();
  return selector.join();
}

/**
 * @type {import('postcss').PluginCreator<void>}
 * @return {import('postcss').Plugin}
 */
function pluginCreator() {
  return {
    postcssPlugin: 'postcss-unique-selectors',
    OnceExit(css) {
      css.walkRules((nodes) => {
        /** @type {string[]} */
        let comments = [];
        /** @type {selectorParser.SyncProcessor<void>} */
        const removeAndSaveComments = (selNode) => {
          selNode.walk((sel) => {
            if (sel.type === 'comment') {
              comments.push(sel.value);
              sel.remove();
              return;
            } else {
              return;
            }
          });
        };
        if (nodes.raws.selector && nodes.raws.selector.raw) {
          parseSelectors(nodes.raws.selector.raw, removeAndSaveComments);
          nodes.raws.selector.raw = unique(nodes);
        }
        nodes.selector = parseSelectors(nodes.selector, removeAndSaveComments);
        nodes.selector = unique(nodes);
        nodes.selectors = nodes.selectors.concat(comments);
      });
    },
  };
}

pluginCreator.postcss = true;
module.exports = pluginCreator;