code:
'use strict';
/* https://www.w3.org/TR/css-color-4/#named-colors */
module.exports = new Set([
  'aliceblue',
  'antiquewhite',
  'aqua',
  'aquamarine',
  'azure',
  'beige',
  'bisque',
  'black',
  'blanchedalmond',
  'blue',
  'blueviolet',
  'brown',
  'burlywood',
  'cadetblue',
  'chartreuse',
  'chocolate',
  'coral',
  'cornflowerblue',
  'cornsilk',
  'crimson',
  'cyan',
  'darkblue',
  'darkcyan',
  'darkgoldenrod',
  'darkgray',
  'darkgreen',
  'darkgrey',
  'darkkhaki',
  'darkmagenta',
  'darkolivegreen',
  'darkorange',
  'darkorchid',
  'darkred',
  'darksalmon',
  'darkseagreen',
  'darkslateblue',
  'darkslategray',
  'darkslategrey',
  'darkturquoise',
  'darkviolet',
  'deeppink',
  'deepskyblue',
  'dimgray',
  'dimgrey',
  'dodgerblue',
  'firebrick',
  'floralwhite',
  'forestgreen',
  'fuchsia',
  'gainsboro',
  'ghostwhite',
  'gold',
  'goldenrod',
  'gray',
  'green',
  'greenyellow',
  'grey',
  'honeydew',
  'hotpink',
  'indianred',
  'indigo',
  'ivory',
  'khaki',
  'lavender',
  'lavenderblush',
  'lawngreen',
  'lemonchiffon',
  'lightblue',
  'lightcoral',
  'lightcyan',
  'lightgoldenrodyellow',
  'lightgray',
  'lightgreen',
  'lightgrey',
  'lightpink',
  'lightsalmon',
  'lightseagreen',
  'lightskyblue',
  'lightslategray',
  'lightslategrey',
  'lightsteelblue',
  'lightyellow',
  'lime',
  'limegreen',
  'linen',
  'magenta',
  'maroon',
  'mediumaquamarine',
  'mediumblue',
  'mediumorchid',
  'mediumpurple',
  'mediumseagreen',
  'mediumslateblue',
  'mediumspringgreen',
  'mediumturquoise',
  'mediumvioletred',
  'midnightblue',
  'mintcream',
  'mistyrose',
  'moccasin',
  'navajowhite',
  'navy',
  'oldlace',
  'olive',
  'olivedrab',
  'orange',
  'orangered',
  'orchid',
  'palegoldenrod',
  'palegreen',
  'paleturquoise',
  'palevioletred',
  'papayawhip',
  'peachpuff',
  'peru',
  'pink',
  'plum',
  'powderblue',
  'purple',
  'rebeccapurple',
  'red',
  'rosybrown',
  'royalblue',
  'saddlebrown',
  'salmon',
  'sandybrown',
  'seagreen',
  'seashell',
  'sienna',
  'silver',
  'skyblue',
  'slateblue',
  'slategray',
  'slategrey',
  'snow',
  'springgreen',
  'steelblue',
  'tan',
  'teal',
  'thistle',
  'tomato',
  'turquoise',
  'violet',
  'wheat',
  'white',
  'whitesmoke',
  'yellow',
  'yellowgreen',
]);


code:
'use strict';
const { list } = require('postcss');
const stylehacks = require('stylehacks');
const insertCloned = require('../insertCloned.js');
const parseTrbl = require('../parseTrbl.js');
const hasAllProps = require('../hasAllProps.js');
const getDecls = require('../getDecls.js');
const getRules = require('../getRules.js');
const getValue = require('../getValue.js');
const mergeRules = require('../mergeRules.js');
const minifyTrbl = require('../minifyTrbl.js');
const minifyWsc = require('../minifyWsc.js');
const canMerge = require('../canMerge.js');
const trbl = require('../trbl.js');
const isCustomProp = require('../isCustomProp.js');
const canExplode = require('../canExplode.js');
const getLastNode = require('../getLastNode.js');
const parseWsc = require('../parseWsc.js');
const { isValidWsc } = require('../validateWsc.js');

const wsc = ['width', 'style', 'color'];
const defaults = ['medium', 'none', 'currentcolor'];
const colorMightRequireFallback =
  /(hsla|rgba|color|hwb|lab|lch|oklab|oklch)\(/i;

/**
 * @param {...string} parts
 * @return {string}
 */
function borderProperty(...parts) {
  return `border-${parts.join('-')}`;
}
/**
 * @param {string} value
 * @return {string}
 */
function mapBorderProperty(value) {
  return borderProperty(value);
}

const directions = trbl.map(mapBorderProperty);
const properties = wsc.map(mapBorderProperty);
/** @type {string[]} */
const directionalProperties = directions.reduce(
  (prev, curr) => prev.concat(wsc.map((prop) => `${curr}-${prop}`)),
  /** @type {string[]} */ ([])
);

const precedence = [
  ['border'],
  directions.concat(properties),
  directionalProperties,
];

const allProperties = precedence.reduce((a, b) => a.concat(b));

/**
 * @param {string} prop
 * @return {number | undefined}
 */
function getLevel(prop) {
  for (let i = 0; i < precedence.length; i++) {
    if (precedence[i].includes(prop.toLowerCase())) {
      return i;
    }
  }
}

/** @type {(value: string) => boolean} */
const isValueCustomProp = (value) =>
  value !== undefined && value.search(/var\s*\(\s*--/i) !== -1;

/**
 * @param {string[]} values
 * @return {boolean}
 */
function canMergeValues(values) {
  return !values.some(isValueCustomProp);
}

/**
 * @param {import('postcss').Declaration} decl
 * @return {string}
 */
function getColorValue(decl) {
  if (decl.prop.substr(-5) === 'color') {
    return decl.value;
  }

  return parseWsc(decl.value)[2] || defaults[2];
}

/**
 * @param {[string, string, string]} values
 * @param {[string, string, string]} nextValues
 * @return {string[]}
 */
function diffingProps(values, nextValues) {
  return wsc.reduce((prev, curr, i) => {
    if (values[i] === nextValues[i]) {
      return prev;
    }

    return [...prev, curr];
  }, /** @type {string[]} */ ([]));
}

/**
 * @param {{values: [string, string, string], nextValues: [string, string, string], decl: import('postcss').Declaration, nextDecl: import('postcss').Declaration, index: number}} arg
 * @return {void}
 */
function mergeRedundant({ values, nextValues, decl, nextDecl, index }) {
  if (!canMerge([decl, nextDecl])) {
    return;
  }

  if (stylehacks.detect(decl) || stylehacks.detect(nextDecl)) {
    return;
  }

  const diff = diffingProps(values, nextValues);

  if (diff.length !== 1) {
    return;
  }

  const prop = /** @type {string} */ (diff.pop());
  const position = wsc.indexOf(prop);

  const prop1 = `${nextDecl.prop}-${prop}`;
  const prop2 = `border-${prop}`;

  let props = parseTrbl(values[position]);

  props[index] = nextValues[position];

  const borderValue2 = values.filter((e, i) => i !== position).join(' ');
  const propValue2 = minifyTrbl(props);

  const origLength = (minifyWsc(decl.value) + nextDecl.prop + nextDecl.value)
    .length;
  const newLength1 =
    decl.value.length + prop1.length + minifyWsc(nextValues[position]).length;
  const newLength2 = borderValue2.length + prop2.length + propValue2.length;

  if (newLength1 < newLength2 && newLength1 < origLength) {
    nextDecl.prop = prop1;
    nextDecl.value = nextValues[position];
  }

  if (newLength2 < newLength1 && newLength2 < origLength) {
    decl.value = borderValue2;
    nextDecl.prop = prop2;
    nextDecl.value = propValue2;
  }
}

/**
 * @param {string | string[]} mapped
 * @return {boolean}
 */
function isCloseEnough(mapped) {
  return (
    (mapped[0] === mapped[1] && mapped[1] === mapped[2]) ||
    (mapped[1] === mapped[2] && mapped[2] === mapped[3]) ||
    (mapped[2] === mapped[3] && mapped[3] === mapped[0]) ||
    (mapped[3] === mapped[0] && mapped[0] === mapped[1])
  );
}

/**
 * @param {string[]} mapped
 * @return {string[]}
 */
function getDistinctShorthands(mapped) {
  return [...new Set(mapped)];
}
/**
 * @param {import('postcss').Rule} rule
 * @return {void}
 */
function explode(rule) {
  rule.walkDecls(/^border/i, (decl) => {
    if (!canExplode(decl, false)) {
      return;
    }

    if (stylehacks.detect(decl)) {
      return;
    }

    const prop = decl.prop.toLowerCase();

    // border -> border-trbl
    if (prop === 'border') {
      if (isValidWsc(parseWsc(decl.value))) {
        directions.forEach((direction) => {
          insertCloned(
            /** @type {import('postcss').Rule} */ (decl.parent),
            decl,
            { prop: direction }
          );
        });

        decl.remove();
      }
    }

    // border-trbl -> border-trbl-wsc
    if (directions.some((direction) => prop === direction)) {
      let values = parseWsc(decl.value);

      if (isValidWsc(values)) {
        wsc.forEach((d, i) => {
          insertCloned(
            /** @type {import('postcss').Rule} */ (decl.parent),
            decl,
            {
              prop: `${prop}-${d}`,
              value: values[i] || defaults[i],
            }
          );
        });

        decl.remove();
      }
    }

    // border-wsc -> border-trbl-wsc
    wsc.some((style) => {
      if (prop !== borderProperty(style)) {
        return false;
      }

      if (isCustomProp(decl)) {
        decl.prop = decl.prop.toLowerCase();
        return false;
      }
      parseTrbl(decl.value).forEach((value, i) => {
        insertCloned(
          /** @type {import('postcss').Rule} */ (decl.parent),
          decl,
          {
            prop: borderProperty(trbl[i], style),
            value,
          }
        );
      });

      return decl.remove();
    });
  });
}

/**
 * @param {import('postcss').Rule} rule
 * @return {void}
 */
function merge(rule) {
  // border-trbl-wsc -> border-trbl
  trbl.forEach((direction) => {
    const prop = borderProperty(direction);

    mergeRules(
      rule,
      wsc.map((style) => borderProperty(direction, style)),
      (rules, lastNode) => {
        if (canMerge(rules, false) && !rules.some(stylehacks.detect)) {
          insertCloned(
            /** @type {import('postcss').Rule} */ (lastNode.parent),
            lastNode,
            {
              prop,
              value: rules.map(getValue).join(' '),
            }
          );
          for (const node of rules) {
            node.remove();
          }

          return true;
        }
        return false;
      }
    );
  });

  // border-trbl-wsc -> border-wsc
  wsc.forEach((style) => {
    const prop = borderProperty(style);

    mergeRules(
      rule,
      trbl.map((direction) => borderProperty(direction, style)),
      (rules, lastNode) => {
        if (canMerge(rules) && !rules.some(stylehacks.detect)) {
          insertCloned(
            /** @type {import('postcss').Rule} */ (lastNode.parent),
            lastNode,
            {
              prop,
              value: minifyTrbl(rules.map(getValue).join(' ')),
            }
          );

          for (const node of rules) {
            node.remove();
          }

          return true;
        }
        return false;
      }
    );
  });

  // border-trbl -> border-wsc
  mergeRules(rule, directions, (rules, lastNode) => {
    if (rules.some(stylehacks.detect)) {
      return false;
    }

    const values = rules.map(({ value }) => value);

    if (!canMergeValues(values)) {
      return false;
    }

    const parsed = values.map((value) => parseWsc(value));

    if (!parsed.every(isValidWsc)) {
      return false;
    }

    wsc.forEach((d, i) => {
      const value = parsed.map((v) => v[i] || defaults[i]);

      if (canMergeValues(value)) {
        insertCloned(
          /** @type {import('postcss').Rule} */ (lastNode.parent),
          lastNode,
          {
            prop: borderProperty(d),
            value: minifyTrbl(
              /** @type {[string, string, string, string]} */ (value)
            ),
          }
        );
      } else {
        insertCloned(
          /** @type {import('postcss').Rule} */ (lastNode.parent),
          lastNode
        );
      }
    });

    for (const node of rules) {
      node.remove();
    }

    return true;
  });

  // border-wsc -> border
  // border-wsc -> border + border-color
  // border-wsc -> border + border-dir
  mergeRules(rule, properties, (rules, lastNode) => {
    if (rules.some(stylehacks.detect)) {
      return false;
    }

    const values = rules.map((node) => parseTrbl(node.value));
    const mapped = [0, 1, 2, 3].map((i) =>
      [values[0][i], values[1][i], values[2][i]].join(' ')
    );

    if (!canMergeValues(mapped)) {
      return false;
    }

    const [width, style, color] = rules;
    const reduced = getDistinctShorthands(mapped);

    if (isCloseEnough(mapped) && canMerge(rules, false)) {
      const first =
        mapped.indexOf(reduced[0]) !== mapped.lastIndexOf(reduced[0]);

      const border = insertCloned(
        /** @type {import('postcss').Rule} */ (lastNode.parent),
        lastNode,
        {
          prop: 'border',
          value: first ? reduced[0] : reduced[1],
        }
      );

      if (reduced[1]) {
        const value = first ? reduced[1] : reduced[0];
        const prop = borderProperty(trbl[mapped.indexOf(value)]);

        rule.insertAfter(
          border,
          Object.assign(lastNode.clone(), {
            prop,
            value,
          })
        );
      }
      for (const node of rules) {
        node.remove();
      }

      return true;
    } else if (reduced.length === 1) {
      rule.insertBefore(
        color,
        Object.assign(lastNode.clone(), {
          prop: 'border',
          value: [width, style].map(getValue).join(' '),
        })
      );
      rules
        .filter((node) => node.prop.toLowerCase() !== properties[2])
        .forEach((node) => node.remove());

      return true;
    }
    return false;
  });

  // border-wsc -> border + border-trbl
  mergeRules(rule, properties, (rules, lastNode) => {
    if (rules.some(stylehacks.detect)) {
      return false;
    }

    const values = rules.map((node) => parseTrbl(node.value));
    const mapped = [0, 1, 2, 3].map((i) =>
      [values[0][i], values[1][i], values[2][i]].join(' ')
    );
    const reduced = getDistinctShorthands(mapped);
    const none = 'medium none currentcolor';

    if (reduced.length > 1 && reduced.length < 4 && reduced.includes(none)) {
      const filtered = mapped.filter((p) => p !== none);
      const mostCommon = reduced.sort(
        (a, b) =>
          mapped.filter((v) => v === b).length -
          mapped.filter((v) => v === a).length
      )[0];
      const borderValue = reduced.length === 2 ? filtered[0] : mostCommon;

      rule.insertBefore(
        lastNode,
        Object.assign(lastNode.clone(), {
          prop: 'border',
          value: borderValue,
        })
      );

      directions.forEach((dir, i) => {
        if (mapped[i] !== borderValue) {
          rule.insertBefore(
            lastNode,
            Object.assign(lastNode.clone(), {
              prop: dir,
              value: mapped[i],
            })
          );
        }
      });

      for (const node of rules) {
        node.remove();
      }

      return true;
    }
    return false;
  });

  // border-trbl -> border
  // border-trbl -> border + border-trbl
  mergeRules(rule, directions, (rules, lastNode) => {
    if (rules.some(stylehacks.detect)) {
      return false;
    }

    const values = rules.map((node) => {
      const wscValue = parseWsc(node.value);

      if (!isValidWsc(wscValue)) {
        return node.value;
      }

      return wscValue.map((value, i) => value || defaults[i]).join(' ');
    });

    const reduced = getDistinctShorthands(values);

    if (isCloseEnough(values)) {
      const first =
        values.indexOf(reduced[0]) !== values.lastIndexOf(reduced[0]);

      rule.insertBefore(
        lastNode,
        Object.assign(lastNode.clone(), {
          prop: 'border',
          value: minifyWsc(first ? values[0] : values[1]),
        })
      );

      if (reduced[1]) {
        const value = first ? reduced[1] : reduced[0];
        const prop = directions[values.indexOf(value)];
        rule.insertBefore(
          lastNode,
          Object.assign(lastNode.clone(), {
            prop: prop,
            value: minifyWsc(value),
          })
        );
      }

      for (const node of rules) {
        node.remove();
      }

      return true;
    }
    return false;
  });

  // border-trbl-wsc + border-trbl (custom prop) -> border-trbl + border-trbl-wsc (custom prop)
  directions.forEach((direction) => {
    wsc.forEach((style, i) => {
      const prop = `${direction}-${style}`;

      mergeRules(rule, [direction, prop], (rules, lastNode) => {
        if (lastNode.prop !== direction) {
          return false;
        }

        const values = parseWsc(lastNode.value);

        if (!isValidWsc(values)) {
          return false;
        }

        const wscProp = rules.filter((r) => r !== lastNode)[0];

        if (!isValueCustomProp(values[i]) || isCustomProp(wscProp)) {
          return false;
        }

        const wscValue = values[i];

        values[i] = wscProp.value;

        if (canMerge(rules, false) && !rules.some(stylehacks.detect)) {
          insertCloned(
            /** @type {import('postcss').Rule} */ (lastNode.parent),
            lastNode,
            {
              prop,
              value: wscValue,
            }
          );
          lastNode.value = minifyWsc(/** @type {any} */ (values));

          wscProp.remove();

          return true;
        }
        return false;
      });
    });
  });

  // border-wsc + border (custom prop) -> border + border-wsc (custom prop)
  wsc.forEach((style, i) => {
    const prop = borderProperty(style);
    mergeRules(rule, ['border', prop], (rules, lastNode) => {
      if (lastNode.prop !== 'border') {
        return false;
      }

      const values = parseWsc(lastNode.value);

      if (!isValidWsc(values)) {
        return false;
      }

      const wscProp = rules.filter((r) => r !== lastNode)[0];

      if (!isValueCustomProp(values[i]) || isCustomProp(wscProp)) {
        return false;
      }

      const wscValue = values[i];

      values[i] = wscProp.value;

      if (canMerge(rules, false) && !rules.some(stylehacks.detect)) {
        insertCloned(
          /** @type {import('postcss').Rule} */ (lastNode.parent),
          lastNode,
          {
            prop,
            value: wscValue,
          }
        );
        lastNode.value = minifyWsc(/** @type {any} */ (values));
        wscProp.remove();

        return true;
      }
      return false;
    });
  });

  // optimize border-trbl
  let decls = getDecls(rule, directions);

  while (decls.length) {
    const lastNode = decls[decls.length - 1];

    wsc.forEach((d, i) => {
      const names = directions
        .filter((name) => name !== lastNode.prop)
        .map((name) => `${name}-${d}`);

      let nodes = rule.nodes.slice(0, rule.nodes.indexOf(lastNode));

      const border = getLastNode(nodes, 'border');

      if (border) {
        nodes = nodes.slice(nodes.indexOf(border));
      }

      const props = nodes.filter(
        (node) =>
          node.type === 'decl' &&
          names.includes(node.prop) &&
          node.important === lastNode.important
      );
      const rules = getRules(
        /** @type {import('postcss').Declaration[]} */ (props),
        names
      );

      if (hasAllProps(rules, ...names) && !rules.some(stylehacks.detect)) {
        const values = rules.map((node) => (node ? node.value : null));
        const filteredValues = values.filter(Boolean);
        const lastNodeValue = list.space(lastNode.value)[i];

        values[directions.indexOf(lastNode.prop)] = lastNodeValue;

        let value = minifyTrbl(values.join(' '));

        if (
          filteredValues[0] === filteredValues[1] &&
          filteredValues[1] === filteredValues[2]
        ) {
          value = /** @type {string} */ (filteredValues[0]);
        }

        let refNode = props[props.length - 1];

        if (value === lastNodeValue) {
          refNode = lastNode;
          let valueArray = list.space(lastNode.value);
          valueArray.splice(i, 1);
          lastNode.value = valueArray.join(' ');
        }

        insertCloned(
          /** @type {import('postcss').Rule} */ (refNode.parent),
          /** @type {import('postcss').Declaration} */ (refNode),
          {
            prop: borderProperty(d),
            value,
          }
        );

        decls = decls.filter((node) => !rules.includes(node));
        for (const node of rules) {
          node.remove();
        }
      }
    });

    decls = decls.filter((node) => node !== lastNode);
  }

  rule.walkDecls('border', (decl) => {
    const nextDecl = decl.next();

    if (!nextDecl || nextDecl.type !== 'decl') {
      return false;
    }

    const index = directions.indexOf(nextDecl.prop);

    if (index === -1) {
      return;
    }

    const values = parseWsc(decl.value);
    const nextValues = parseWsc(nextDecl.value);

    if (!isValidWsc(values) || !isValidWsc(nextValues)) {
      return;
    }

    const config = {
      values,
      nextValues,
      decl,
      nextDecl,
      index,
    };

    return mergeRedundant(config);
  });

  rule.walkDecls(/^border($|-(top|right|bottom|left)$)/i, (decl) => {
    let values = parseWsc(decl.value);

    if (!isValidWsc(values)) {
      return;
    }

    const position = directions.indexOf(decl.prop);
    let dirs = [...directions];

    dirs.splice(position, 1);
    wsc.forEach((d, i) => {
      const props = dirs.map((dir) => `${dir}-${d}`);

      mergeRules(rule, [decl.prop, ...props], (rules) => {
        if (!rules.includes(decl)) {
          return false;
        }

        const longhands = rules.filter((p) => p !== decl);

        if (
          longhands[0].value.toLowerCase() ===
            longhands[1].value.toLowerCase() &&
          longhands[1].value.toLowerCase() ===
            longhands[2].value.toLowerCase() &&
          values[i] !== undefined &&
          longhands[0].value.toLowerCase() === values[i].toLowerCase()
        ) {
          for (const node of longhands) {
            node.remove();
          }

          insertCloned(
            /** @type {import('postcss').Rule} */ (decl.parent),
            decl,
            {
              prop: borderProperty(d),
              value: values[i],
            }
          );

          /** @type {string|null} */ (values[i]) = null;
        }
        return false;
      });

      const newValue = values.join(' ');

      if (newValue) {
        decl.value = newValue;
      } else {
        decl.remove();
      }
    });
  });

  // clean-up values
  rule.walkDecls(/^border($|-(top|right|bottom|left)$)/i, (decl) => {
    decl.value = minifyWsc(decl.value);
  });

  // border-spacing-hv -> border-spacing
  rule.walkDecls(/^border-spacing$/i, (decl) => {
    const value = list.space(decl.value);

    // merge vertical and horizontal dups
    if (value.length > 1 && value[0] === value[1]) {
      decl.value = value.slice(1).join(' ');
    }
  });

  // clean-up rules
  decls = getDecls(rule, allProperties);

  while (decls.length) {
    const lastNode = decls[decls.length - 1];
    const lastPart = lastNode.prop.split('-').pop();

    // remove properties of lower precedence
    const lesser = decls.filter(
      (node) =>
        !stylehacks.detect(lastNode) &&
        !stylehacks.detect(node) &&
        !isCustomProp(lastNode) &&
        node !== lastNode &&
        node.important === lastNode.important &&
        /** @type {number} */ (getLevel(node.prop)) >
          /** @type {number} */ (getLevel(lastNode.prop)) &&
        (node.prop.toLowerCase().includes(lastNode.prop) ||
          node.prop.toLowerCase().endsWith(/** @type {string} */ (lastPart)))
    );

    for (const node of lesser) {
      node.remove();
    }
    decls = decls.filter((node) => !lesser.includes(node));

    // get duplicate properties
    let duplicates = decls.filter(
      (node) =>
        !stylehacks.detect(lastNode) &&
        !stylehacks.detect(node) &&
        node !== lastNode &&
        node.important === lastNode.important &&
        node.prop === lastNode.prop &&
        !(!isCustomProp(node) && isCustomProp(lastNode))
    );

    if (duplicates.length) {
      if (colorMightRequireFallback.test(getColorValue(lastNode))) {
        const preserve = duplicates
          .filter(
            (node) => !colorMightRequireFallback.test(getColorValue(node))
          )
          .pop();

        duplicates = duplicates.filter((node) => node !== preserve);
      }
      for (const node of duplicates) {
        node.remove();
      }
    }

    decls = decls.filter(
      (node) => node !== lastNode && !duplicates.includes(node)
    );
  }
}

module.exports = {
  explode,
  merge,
};

license: mit


code:
'use strict';
const stylehacks = require('stylehacks');
const canMerge = require('../canMerge.js');
const getDecls = require('../getDecls.js');
const minifyTrbl = require('../minifyTrbl.js');
const parseTrbl = require('../parseTrbl.js');
const insertCloned = require('../insertCloned.js');
const mergeRules = require('../mergeRules.js');
const mergeValues = require('../mergeValues.js');
const trbl = require('../trbl.js');
const isCustomProp = require('../isCustomProp.js');
const canExplode = require('../canExplode.js');

/**
 * @param {string} prop
 * @return {{explode: (rule: import('postcss').Rule) => void, merge: (rule: import('postcss').Rule) => void}}
 */
module.exports = (prop) => {
  const properties = trbl.map((direction) => `${prop}-${direction}`);
  /** @type {(rule: import('postcss').Rule) => void} */
  const cleanup = (rule) => {
    let decls = getDecls(rule, [prop].concat(properties));

    while (decls.length) {
      const lastNode = decls[decls.length - 1];

      // remove properties of lower precedence
      const lesser = decls.filter(
        (node) =>
          !stylehacks.detect(lastNode) &&
          !stylehacks.detect(node) &&
          node !== lastNode &&
          node.important === lastNode.important &&
          lastNode.prop === prop &&
          node.prop !== lastNode.prop
      );

      for (const node of lesser) {
        node.remove();
      }
      decls = decls.filter((node) => !lesser.includes(node));

      // get duplicate properties
      let duplicates = decls.filter(
        (node) =>
          !stylehacks.detect(lastNode) &&
          !stylehacks.detect(node) &&
          node !== lastNode &&
          node.important === lastNode.important &&
          node.prop === lastNode.prop &&
          !(!isCustomProp(node) && isCustomProp(lastNode))
      );

      for (const node of duplicates) {
        node.remove();
      }
      decls = decls.filter(
        (node) => node !== lastNode && !duplicates.includes(node)
      );
    }
  };

  const processor = {
    /** @type {(rule: import('postcss').Rule) => void} */
    explode: (rule) => {
      rule.walkDecls(new RegExp('^' + prop + '$', 'i'), (decl) => {
        if (!canExplode(decl)) {
          return;
        }

        if (stylehacks.detect(decl)) {
          return;
        }

        const values = parseTrbl(decl.value);

        trbl.forEach((direction, index) => {
          insertCloned(
            /** @type {import('postcss').Rule} */ (decl.parent),
            decl,
            {
              prop: properties[index],
              value: values[index],
            }
          );
        });

        decl.remove();
      });
    },
    /** @type {(rule: import('postcss').Rule) => void} */
    merge: (rule) => {
      mergeRules(rule, properties, (rules, lastNode) => {
        if (canMerge(rules) && !rules.some(stylehacks.detect)) {
          insertCloned(
            /** @type {import('postcss').Rule} */ (lastNode.parent),
            lastNode,
            {
              prop,
              value: minifyTrbl(mergeValues(...rules)),
            }
          );
          for (const node of rules) {
            node.remove();
          }

          return true;
        }
        return false;
      });

      cleanup(rule);
    },
  };

  return processor;
};


code:
'use strict';
const { list } = require('postcss');
const { unit } = require('postcss-value-parser');
const stylehacks = require('stylehacks');
const canMerge = require('../canMerge.js');
const getDecls = require('../getDecls.js');
const getValue = require('../getValue.js');
const mergeRules = require('../mergeRules.js');
const insertCloned = require('../insertCloned.js');
const isCustomProp = require('../isCustomProp.js');
const canExplode = require('../canExplode.js');

const properties = ['column-width', 'column-count'];
const auto = 'auto';
const inherit = 'inherit';

/**
 * Normalize a columns shorthand definition. Both of the longhand
 * properties' initial values are 'auto', and as per the spec,
 * omitted values are set to their initial values. Thus, we can
 * remove any 'auto' definition when there are two values.
 *
 * Specification link: https://www.w3.org/TR/css3-multicol/
 *
 * @param {[string, string]} values
 * @return {string}
 */
function normalize(values) {
  if (values[0].toLowerCase() === auto) {
    return values[1];
  }

  if (values[1].toLowerCase() === auto) {
    return values[0];
  }

  if (
    values[0].toLowerCase() === inherit &&
    values[1].toLowerCase() === inherit
  ) {
    return inherit;
  }

  return values.join(' ');
}
/**
 * @param {import('postcss').Rule} rule
 * @return {void}
 */
function explode(rule) {
  rule.walkDecls(/^columns$/i, (decl) => {
    if (!canExplode(decl)) {
      return;
    }

    if (stylehacks.detect(decl)) {
      return;
    }

    let values = list.space(decl.value);

    if (values.length === 1) {
      values.push(auto);
    }

    values.forEach((value, i) => {
      let prop = properties[1];
      const dimension = unit(value);
      if (value.toLowerCase() === auto) {
        prop = properties[i];
      } else if (dimension && dimension.unit !== '') {
        prop = properties[0];
      }

      insertCloned(/** @type {import('postcss').Rule} */ (decl.parent), decl, {
        prop,
        value,
      });
    });

    decl.remove();
  });
}

/**
 * @param {import('postcss').Rule} rule
 * @return {void}
 */
function cleanup(rule) {
  let decls = getDecls(rule, ['columns'].concat(properties));

  while (decls.length) {
    const lastNode = decls[decls.length - 1];

    // remove properties of lower precedence
    const lesser = decls.filter(
      (node) =>
        !stylehacks.detect(lastNode) &&
        !stylehacks.detect(node) &&
        node !== lastNode &&
        node.important === lastNode.important &&
        lastNode.prop === 'columns' &&
        node.prop !== lastNode.prop
    );

    for (const node of lesser) {
      node.remove();
    }
    decls = decls.filter((node) => !lesser.includes(node));

    // get duplicate properties
    let duplicates = decls.filter(
      (node) =>
        !stylehacks.detect(lastNode) &&
        !stylehacks.detect(node) &&
        node !== lastNode &&
        node.important === lastNode.important &&
        node.prop === lastNode.prop &&
        !(!isCustomProp(node) && isCustomProp(lastNode))
    );

    for (const node of duplicates) {
      node.remove();
    }
    decls = decls.filter(
      (node) => node !== lastNode && !duplicates.includes(node)
    );
  }
}

/**
 * @param {import('postcss').Rule} rule
 * @return {void}
 */
function merge(rule) {
  mergeRules(rule, properties, (rules, lastNode) => {
    if (canMerge(rules) && !rules.some(stylehacks.detect)) {
      insertCloned(
        /** @type {import('postcss').Rule} */ (lastNode.parent),
        lastNode,
        {
          prop: 'columns',
          value: normalize(/** @type [string, string] */ (rules.map(getValue))),
        }
      );

      for (const node of rules) {
        node.remove();
      }

      return true;
    }
    return false;
  });

  cleanup(rule);
}

module.exports = {
  explode,
  merge,
};


code:
'use strict';
const borders = require('./borders');
const columns = require('./columns');
const margin = require('./margin');
const padding = require('./padding');

module.exports = [borders, columns, margin, padding];


code:
'use strict';
const base = require('./boxBase.js');

module.exports = base('margin');


code:
'use strict';
const base = require('./boxBase');

module.exports = base('padding');


code:
'use strict';
/**
 * @param {import('postcss').Rule} rule
 * @param {string[]} properties
 * @return {import('postcss').Declaration[]}
 */
module.exports = function getDecls(rule, properties) {
  return /** @type {import('postcss').Declaration[]} */ (
    rule.nodes.filter(
      (node) =>
        node.type === 'decl' && properties.includes(node.prop.toLowerCase())
    )
  );
};


code:
'use strict';
/** @type {(rule: import('postcss').AnyNode[], prop: string) => import('postcss').Declaration} */
module.exports = (rule, prop) => {
  return /** @type {import('postcss').Declaration} */ (
    rule.filter((n) => n.type === 'decl' && n.prop.toLowerCase() === prop).pop()
  );
};


code:
'use strict';
const getLastNode = require('./getLastNode.js');

/**
 * @param {import('postcss').Declaration[]} props
 * @param {string[]} properties
 * @return {import('postcss').Declaration[]}
 */
module.exports = function getRules(props, properties) {
  return properties
    .map((property) => {
      return getLastNode(props, property);
    })
    .filter(Boolean);
};


code:
'use strict';
/**
 * @param {import('postcss').Declaration} arg
 * @return {string}
 */
module.exports = function getValue({ value }) {
  return value;
};


code:
'use strict';
/** @type {(rule: import('postcss').Declaration[], ...props: string[]) => boolean} */
module.exports = (rule, ...props) => {
  return props.every((p) =>
    rule.some((node) => node.prop && node.prop.toLowerCase().includes(p))
  );
};


code:
'use strict';
/**
 * @param {import('postcss').Rule} rule
 * @param {import('postcss').Declaration} decl
 * @param {Partial<import('postcss').DeclarationProps>=} props
 * @return {import('postcss').Declaration}
 */
module.exports = function insertCloned(rule, decl, props) {
  const newNode = Object.assign(decl.clone(), props);

  rule.insertAfter(decl, newNode);

  return newNode;
};


code:
'use strict';
/** @type {(node: import('postcss').Declaration) => boolean} */
module.exports = (node) => node.value.search(/var\s*\(\s*--/i) !== -1;


code:
'use strict';
const hasAllProps = require('./hasAllProps.js');
const getDecls = require('./getDecls.js');
const getRules = require('./getRules.js');

/**
 * @param {import('postcss').Declaration} propA
 * @param {import('postcss').Declaration} propB
 * @return {boolean}
 */
function isConflictingProp(propA, propB) {
  if (
    !propB.prop ||
    propB.important !== propA.important ||
    propA.prop === propB.prop
  ) {
    return false;
  }

  const partsA = propA.prop.split('-');
  const partsB = propB.prop.split('-');

  /* Be safe: check that the first part matches. So we don't try to
   * combine e.g. border-color and color.
   */
  if (partsA[0] !== partsB[0]) {
    return false;
  }

  const partsASet = new Set(partsA);
  return partsB.every((partB) => partsASet.has(partB));
}

/**
 * @param {import('postcss').Declaration[]} match
 * @param {import('postcss').Declaration[]} nodes
 * @return {boolean}
 */
function hasConflicts(match, nodes) {
  const firstNode = Math.min(...match.map((n) => nodes.indexOf(n)));
  const lastNode = Math.max(...match.map((n) => nodes.indexOf(n)));
  const between = nodes.slice(firstNode + 1, lastNode);

  return match.some((a) => between.some((b) => isConflictingProp(a, b)));
}

/**
 * @param {import('postcss').Rule} rule
 * @param {string[]} properties
 * @param {(rules: import('postcss').Declaration[], last: import('postcss').Declaration, props: import('postcss').Declaration[]) => boolean} callback
 * @return {void}
 */
module.exports = function mergeRules(rule, properties, callback) {
  let decls = getDecls(rule, properties);

  while (decls.length) {
    const last = decls[decls.length - 1];
    const props = decls.filter((node) => node.important === last.important);
    const rules = getRules(props, properties);

    if (
      hasAllProps(rules, ...properties) &&
      !hasConflicts(
        rules,
        /** @type import('postcss').Declaration[]*/ (rule.nodes)
      )
    ) {
      if (callback(rules, last, props)) {
        decls = decls.filter((node) => !rules.includes(node));
      }
    }

    decls = decls.filter((node) => node !== last);
  }
};


code:
'use strict';
const getValue = require('./getValue.js');

/** @type {(...rules: import('postcss').Declaration[]) => string} */
module.exports = (...rules) => rules.map(getValue).join(' ');


code:
'use strict';
const parseTrbl = require('./parseTrbl.js');

/** @type {(v: string | [string, string, string, string]) => string} */
module.exports = (v) => {
  const value = parseTrbl(v);

  if (value[3] === value[1]) {
    value.pop();

    if (value[2] === value[0]) {
      value.pop();

      if (value[0] === value[1]) {
        value.pop();
      }
    }
  }

  return value.join(' ');
};


code:
'use strict';
const parseWsc = require('./parseWsc.js');
const minifyTrbl = require('./minifyTrbl.js');
const { isValidWsc } = require('./validateWsc.js');

const defaults = ['medium', 'none', 'currentcolor'];

/** @type {(v: string) => string} */
module.exports = (v) => {
  const values = parseWsc(v);

  if (!isValidWsc(values)) {
    return minifyTrbl(v);
  }

  const value = [...values, '']
    .reduceRight((prev, cur, i, arr) => {
      if (
        cur === undefined ||
        (cur.toLowerCase() === defaults[i] &&
          (!i || (arr[i - 1] || '').toLowerCase() !== cur.toLowerCase()))
      ) {
        return prev;
      }

      return cur + ' ' + prev;
    })
    .trim();

  return minifyTrbl(value || 'none');
};


code:
'use strict';
const { list } = require('postcss');
/** @type {(v: string | string[]) => [string, string, string, string]} */
module.exports = (v) => {
  const s = typeof v === 'string' ? list.space(v) : v;
  return [
    s[0], // top
    s[1] || s[0], // right
    s[2] || s[0], // bottom
    s[3] || s[1] || s[0], // left
  ];
};


code:
'use strict';
const { list } = require('postcss');
const { isWidth, isStyle, isColor } = require('./validateWsc.js');

const none = /^\s*(none|medium)(\s+none(\s+(none|currentcolor))?)?\s*$/i;

/* Approximate https://drafts.csswg.org/css-values-4/#typedef-dashed-ident */
// eslint-disable-next-line no-control-regex
const varRE = /--(\w|-|[^\x00-\x7F])+/g;
/** @type {(v: string) => string} */
const toLower = (v) => {
  let match;
  let lastIndex = 0;
  let result = '';
  varRE.lastIndex = 0;
  while ((match = varRE.exec(v)) !== null) {
    if (match.index > lastIndex) {
      result += v.substring(lastIndex, match.index).toLowerCase();
    }
    result += match[0];
    lastIndex = match.index + match[0].length;
  }
  if (lastIndex < v.length) {
    result += v.substring(lastIndex).toLowerCase();
  }
  if (result === '') {
    return v;
  }
  return result;
};

/**
 * @param {string} value
 * @return {[string, string, string]}
 */
module.exports = function parseWsc(value) {
  if (none.test(value)) {
    return ['medium', 'none', 'currentcolor'];
  }

  let width, style, color;

  const values = list.space(value);
  if (
    values.length > 1 &&
    isStyle(values[1]) &&
    values[0].toLowerCase() === 'none'
  ) {
    values.unshift();
    width = '0';
  }

  /** @type {string[]} */
  const unknown = [];

  values.forEach((v) => {
    if (isStyle(v)) {
      style = toLower(v);
    } else if (isWidth(v)) {
      width = toLower(v);
    } else if (isColor(v)) {
      color = toLower(v);
    } else {
      unknown.push(v);
    }
  });

  if (unknown.length) {
    if (!width && style && color) {
      width = unknown.pop();
    }

    if (width && !style && color) {
      style = unknown.pop();
    }

    if (width && style && !color) {
      color = unknown.pop();
    }
  }

  return /** @type {[string, string, string]} */ ([width, style, color]);
};


code:
'use strict';
module.exports = ['top', 'right', 'bottom', 'left'];


code:
'use strict';
const colors = require('./colornames.js');

const widths = new Set(['thin', 'medium', 'thick']);
const styles = new Set([
  'none',
  'hidden',
  'dotted',
  'dashed',
  'solid',
  'double',
  'groove',
  'ridge',
  'inset',
  'outset',
]);

/**
 * @param {string} value
 * @return {boolean}
 */
function isStyle(value) {
  return value !== undefined && styles.has(value.toLowerCase());
}

/**
 * @param {string} value
 * @return {boolean}
 */
function isWidth(value) {
  return (
    (value && widths.has(value.toLowerCase())) ||
    /^(\d+(\.\d+)?|\.\d+)(\w+)?$/.test(value)
  );
}

/**
 * @param {string} value
 * @return {boolean}
 */
function isColor(value) {
  if (!value) {
    return false;
  }

  value = value.toLowerCase();

  if (/rgba?\(/.test(value)) {
    return true;
  }

  if (/hsla?\(/.test(value)) {
    return true;
  }

  if (/#([0-9a-z]{6}|[0-9a-z]{3})/.test(value)) {
    return true;
  }

  if (value === 'transparent') {
    return true;
  }

  if (value === 'currentcolor') {
    return true;
  }

  return colors.has(value);
}

/**
 * @param {[string, string, string]} wscs
 * @return {boolean}
 */
function isValidWsc(wscs) {
  const validWidth = isWidth(wscs[0]);
  const validStyle = isStyle(wscs[1]);
  const validColor = isColor(wscs[2]);

  return (
    (validWidth && validStyle) ||
    (validWidth && validColor) ||
    (validStyle && validColor)
  );
}

module.exports = { isStyle, isWidth, isColor, isValidWsc };


code:
export = pluginCreator;
/**
 * @type {import('postcss').PluginCreator<void>}
 * @return {import('postcss').Plugin}
 */
declare function pluginCreator(): import('postcss').Plugin;
declare namespace pluginCreator {
    const postcss: true;
}


code:
declare const _exports: (prop: import('postcss').Declaration, includeCustomProps?: boolean) => boolean;
export = _exports;


code:
declare const _exports: (props: import('postcss').Declaration[], includeCustomProps?: boolean) => boolean;
export = _exports;


code:
declare const _exports: Set<string>;
export = _exports;


code:
/**
 * @param {import('postcss').Rule} rule
 * @return {void}
 */
export function explode(rule: import('postcss').Rule): void;
/**
 * @param {import('postcss').Rule} rule
 * @return {void}
 */
export function merge(rule: import('postcss').Rule): void;


code:
declare function _exports(prop: string): {
    explode: (rule: import('postcss').Rule) => void;
    merge: (rule: import('postcss').Rule) => void;
};
export = _exports;


code:
declare const _exports: (typeof borders)[];
export = _exports;
import borders = require("./borders");


code:
declare const _exports: {
    explode: (rule: import("postcss").Rule) => void;
    merge: (rule: import("postcss").Rule) => void;
};
export = _exports;


code:
declare function _exports(rule: import('postcss').Rule, properties: string[]): import('postcss').Declaration[];
export = _exports;


code:
declare const _exports: (rule: import('postcss').AnyNode[], prop: string) => import('postcss').Declaration;
export = _exports;


code:
declare function _exports(props: import('postcss').Declaration[], properties: string[]): import('postcss').Declaration[];
export = _exports;


code:
declare function _exports({ value }: import('postcss').Declaration): string;
export = _exports;


code:
declare const _exports: (rule: import('postcss').Declaration[], ...props: string[]) => boolean;
export = _exports;


code:
declare function _exports(rule: import('postcss').Rule, decl: import('postcss').Declaration, props?: Partial<import('postcss').DeclarationProps> | undefined): import('postcss').Declaration;
export = _exports;


code:
declare const _exports: (node: import('postcss').Declaration) => boolean;
export = _exports;


code:
declare function _exports(rule: import('postcss').Rule, properties: string[], callback: (rules: import('postcss').Declaration[], last: import('postcss').Declaration, props: import('postcss').Declaration[]) => boolean): void;
export = _exports;


code:
declare const _exports: (...rules: import('postcss').Declaration[]) => string;
export = _exports;


code:
declare const _exports: (v: string | [string, string, string, string]) => string;
export = _exports;


code:
declare const _exports: (v: string) => string;
export = _exports;


code:
declare const _exports: (v: string | string[]) => [string, string, string, string];
export = _exports;


code:
declare function _exports(value: string): [string, string, string];
export = _exports;


code:
declare function _exports(node: import('postcss').Node): import('postcss').Node;
export = _exports;


code:
declare const _exports: string[];
export = _exports;


code:
/**
 * @param {string} value
 * @return {boolean}
 */
export function isStyle(value: string): boolean;
/**
 * @param {string} value
 * @return {boolean}
 */
export function isWidth(value: string): boolean;
/**
 * @param {string} value
 * @return {boolean}
 */
export function isColor(value: string): boolean;
/**
 * @param {[string, string, string]} wscs
 * @return {boolean}
 */
export function isValidWsc(wscs: [string, string, string]): boolean;


code:
Copyright (c) Ben Briggs <beneb.info@gmail.com> (http://beneb.info)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.


code:
# [postcss][postcss]-merge-rules

> Merge CSS rules with PostCSS.

## Install

With [npm](https://npmjs.org/package/postcss-merge-rules) do:

```
npm install postcss-merge-rules --save
```

## Examples

This module will attempt to merge *adjacent* CSS rules:

### By declarations

#### Input

```css
a {
    color: blue;
    font-weight: bold
}

p {
    color: blue;
    font-weight: bold
}
```

#### Output

```css
a,p {
    color: blue;
    font-weight: bold
}
```

### By selectors

#### Input

```css
a {
    color: blue
}

a {
    font-weight: bold
}
```

#### Output

```css
a {
    color: blue;
    font-weight: bold
}
```

### By partial declarations

#### Input

```css
a {
    font-weight: bold
}

p {
    color: blue;
    font-weight: bold
}
```

#### Output

```css
a,p {
    font-weight: bold
}

p {
    color: blue
}
```

## Usage

See the [PostCSS documentation](https://github.com/postcss/postcss#usage) for
examples for your environment.

## Contributors

See [CONTRIBUTORS.md](https://github.com/cssnano/cssnano/blob/master/CONTRIBUTORS.md).

## License

MIT © [Ben Briggs](http://beneb.info)

[postcss]: https://github.com/postcss/postcss


postcss-merge-rules

description: Merge CSS rules with PostCSS.

files: LICENSE-MIT src types

keywords: css optimise postcss postcss-plugin

license: MIT

repository: cssnano/cssnano


code:
'use strict';
const browserslist = require('browserslist');
const { sameParent } = require('cssnano-utils');
const {
  ensureCompatibility,
  sameVendor,
  noVendor,
} = require('./lib/ensureCompatibility');

/**
 * @param {import('postcss').Declaration} a
 * @param {import('postcss').Declaration} b
 * @return {boolean}
 */
function declarationIsEqual(a, b) {
  return (
    a.important === b.important && a.prop === b.prop && a.value === b.value
  );
}

/**
 * @param {import('postcss').Declaration[]} array
 * @param {import('postcss').Declaration} decl
 * @return {number}
 */
function indexOfDeclaration(array, decl) {
  return array.findIndex((d) => declarationIsEqual(d, decl));
}

/**
 * Returns filtered array of matched or unmatched declarations
 * @param {import('postcss').Declaration[]} a
 * @param {import('postcss').Declaration[]} b
 * @param {boolean} [not=false]
 * @return {import('postcss').Declaration[]}
 */
function intersect(a, b, not) {
  return a.filter((c) => {
    const index = indexOfDeclaration(b, c) !== -1;
    return not ? !index : index;
  });
}

/**
 * @param {import('postcss').Declaration[]} a
 * @param {import('postcss').Declaration[]} b
 * @return {boolean}
 */
function sameDeclarationsAndOrder(a, b) {
  if (a.length !== b.length) {
    return false;
  }
  return a.every((d, index) => declarationIsEqual(d, b[index]));
}

/**
 * @param {import('postcss').Rule} ruleA
 * @param {import('postcss').Rule} ruleB
 * @param {string[]=} browsers
 * @param {Map<string, boolean>=} compatibilityCache
 * @return {boolean}
 */
function canMerge(ruleA, ruleB, browsers, compatibilityCache) {
  const a = ruleA.selectors;
  const b = ruleB.selectors;

  const selectors = a.concat(b);

  if (!ensureCompatibility(selectors, browsers, compatibilityCache)) {
    return false;
  }

  const parent = sameParent(
    /** @type {any} */ (ruleA),
    /** @type {any} */ (ruleB)
  );
  if (
    parent &&
    ruleA.parent &&
    ruleA.parent.type === 'atrule' &&
    /** @type {import('postcss').AtRule} */ (ruleA.parent).name.includes(
      'keyframes'
    )
  ) {
    return false;
  }
  return parent && (selectors.every(noVendor) || sameVendor(a, b));
}

/**
 * @param {import('postcss').ChildNode} node
 * @return {node is import('postcss').Declaration}
 */
function isDeclaration(node) {
  return node.type === 'decl';
}
/**
 * @param {import('postcss').Rule} rule
 * @return {import('postcss').Declaration[]}
 */
function getDecls(rule) {
  return rule.nodes.filter(isDeclaration);
}

/** @type {(...rules: import('postcss').Rule[]) => string} */
const joinSelectors = (...rules) => rules.map((s) => s.selector).join();

/**
 * @param {...import('postcss').Rule} rules
 * @return {number}
 */
function ruleLength(...rules) {
  return rules.map((r) => (r.nodes.length ? String(r) : '')).join('').length;
}

/**
 * @param {string} prop
 * @return {{prefix: string?, base:string?, rest:string[]}}
 */
function splitProp(prop) {
  // Treat vendor prefixed properties as if they were unprefixed;
  // moving them when combined with non-prefixed properties can
  // cause issues. e.g. moving -webkit-background-clip when there
  // is a background shorthand definition.

  const parts = prop.split('-');
  if (prop[0] !== '-') {
    return {
      prefix: '',
      base: parts[0],
      rest: parts.slice(1),
    };
  }
  // Don't split css variables
  if (prop[1] === '-') {
    return {
      prefix: null,
      base: null,
      rest: [prop],
    };
  }
  // Found prefix
  return {
    prefix: parts[1],
    base: parts[2],
    rest: parts.slice(3),
  };
}

/**
 * @param {string} propA
 * @param {string} propB
 * @return {boolean}
 */
function isConflictingProp(propA, propB) {
  if (propA === propB) {
    // Same specificity
    return true;
  }
  const a = splitProp(propA);
  const b = splitProp(propB);
  // Don't resort css variables
  if (!a.base && !b.base) {
    return true;
  }

  // Different base and none is `place`;
  if (a.base !== b.base && a.base !== 'place' && b.base !== 'place') {
    return false;
  }

  // Conflict if rest-count mismatches
  if (a.rest.length !== b.rest.length) {
    return true;
  }

  /* Do not merge conflicting border properties */
  if (a.base === 'border') {
    const allRestProps = new Set([...a.rest, ...b.rest]);
    if (
      allRestProps.has('image') ||
      allRestProps.has('width') ||
      allRestProps.has('color') ||
      allRestProps.has('style')
    ) {
      return true;
    }
  }
  // Conflict if rest parameters are equal (same but unprefixed)
  return a.rest.every((s, index) => b.rest[index] === s);
}

/**
 * @param {import('postcss').Rule} first
 * @param {import('postcss').Rule} second
 * @return {boolean} merged
 */
function mergeParents(first, second) {
  // Null check for detached rules
  if (!first.parent || !second.parent) {
    return false;
  }

  // Check if parents share node
  if (first.parent === second.parent) {
    return false;
  }

  // sameParent() already called by canMerge()

  second.remove();
  first.parent.append(second);
  return true;
}

/**
 * @param {import('postcss').Rule} first
 * @param {import('postcss').Rule} second
 * @return {import('postcss').Rule} mergedRule
 */
function partialMerge(first, second) {
  let intersection = intersect(getDecls(first), getDecls(second));
  if (intersection.length === 0) {
    return second;
  }
  let nextRule = second.next();
  if (!nextRule) {
    // Grab next cousin
    /** @type {any} */
    const parentSibling =
      /** @type {import('postcss').Container<import('postcss').ChildNode>} */ (
        second.parent
      ).next();
    nextRule = parentSibling && parentSibling.nodes && parentSibling.nodes[0];
  }
  if (nextRule && nextRule.type === 'rule' && canMerge(second, nextRule)) {
    let nextIntersection = intersect(getDecls(second), getDecls(nextRule));
    if (nextIntersection.length > intersection.length) {
      mergeParents(second, nextRule);
      first = second;
      second = nextRule;
      intersection = nextIntersection;
    }
  }

  const firstDecls = getDecls(first);
  // Filter out intersections with later conflicts in First
  intersection = intersection.filter((decl, intersectIndex) => {
    const indexOfDecl = indexOfDeclaration(firstDecls, decl);
    const nextConflictInFirst = firstDecls
      .slice(indexOfDecl + 1)
      .filter((d) => isConflictingProp(d.prop, decl.prop));
    if (nextConflictInFirst.length === 0) {
      return true;
    }
    const nextConflictInIntersection = intersection
      .slice(intersectIndex + 1)
      .filter((d) => isConflictingProp(d.prop, decl.prop));
    if (nextConflictInFirst.length !== nextConflictInIntersection.length) {
      return false;
    }
    return nextConflictInFirst.every((d, index) =>
      declarationIsEqual(d, nextConflictInIntersection[index])
    );
  });

  // Filter out intersections with previous conflicts in Second
  const secondDecls = getDecls(second);
  intersection = intersection.filter((decl) => {
    const nextConflictIndex = secondDecls.findIndex((d) =>
      isConflictingProp(d.prop, decl.prop)
    );
    if (nextConflictIndex === -1) {
      return false;
    }
    if (!declarationIsEqual(secondDecls[nextConflictIndex], decl)) {
      return false;
    }
    if (
      decl.prop.toLowerCase() !== 'direction' &&
      decl.prop.toLowerCase() !== 'unicode-bidi' &&
      secondDecls.some(
        (declaration) => declaration.prop.toLowerCase() === 'all'
      )
    ) {
      return false;
    }
    secondDecls.splice(nextConflictIndex, 1);
    return true;
  });

  if (intersection.length === 0) {
    // Nothing to merge
    return second;
  }

  const receivingBlock = second.clone();
  receivingBlock.selector = joinSelectors(first, second);
  receivingBlock.nodes = [];

  /** @type {import('postcss').Container<import('postcss').ChildNode>} */ (
    second.parent
  ).insertBefore(second, receivingBlock);

  const firstClone = first.clone();
  const secondClone = second.clone();

  /**
   * @param {function(import('postcss').Declaration):void} callback
   * @this {import('postcss').Rule}
   * @return {function(import('postcss').Declaration)}
   */
  function moveDecl(callback) {
    return (decl) => {
      if (indexOfDeclaration(intersection, decl) !== -1) {
        callback.call(this, decl);
      }
    };
  }
  firstClone.walkDecls(
    moveDecl((decl) => {
      decl.remove();
      receivingBlock.append(decl);
    })
  );
  secondClone.walkDecls(moveDecl((decl) => decl.remove()));
  const merged = ruleLength(firstClone, receivingBlock, secondClone);
  const original = ruleLength(first, second);
  if (merged < original) {
    first.replaceWith(firstClone);
    second.replaceWith(secondClone);
    [firstClone, receivingBlock, secondClone].forEach((r) => {
      if (r.nodes.length === 0) {
        r.remove();
      }
    });
    if (!secondClone.parent) {
      return receivingBlock;
    }
    return secondClone;
  } else {
    receivingBlock.remove();
    return second;
  }
}

/**
 * @param {string[]} browsers
 * @param {Map<string, boolean>} compatibilityCache
 * @return {function(import('postcss').Rule)}
 */
function selectorMerger(browsers, compatibilityCache) {
  /** @type {import('postcss').Rule | null} */
  let cache = null;
  return function (rule) {
    // Prime the cache with the first rule, or alternately ensure that it is
    // safe to merge both declarations before continuing
    if (!cache || !canMerge(rule, cache, browsers, compatibilityCache)) {
      cache = rule;
      return;
    }
    // Ensure that we don't deduplicate the same rule; this is sometimes
    // caused by a partial merge
    if (cache === rule) {
      cache = rule;
      return;
    }

    // Parents merge: check if the rules have same parents, but not same parent nodes
    mergeParents(cache, rule);

    // Merge when declarations are exactly equal
    // e.g. h1 { color: red } h2 { color: red }
    if (sameDeclarationsAndOrder(getDecls(rule), getDecls(cache))) {
      rule.selector = joinSelectors(cache, rule);
      cache.remove();
      cache = rule;
      return;
    }
    // Merge when both selectors are exactly equal
    // e.g. a { color: blue } a { font-weight: bold }
    if (cache.selector === rule.selector) {
      const cached = getDecls(cache);
      rule.walk((node) => {
        if (node.type === 'decl' && indexOfDeclaration(cached, node) !== -1) {
          node.remove();
          return;
        }
        /** @type {import('postcss').Rule} */ (cache).append(node);
      });
      rule.remove();
      return;
    }
    // Partial merge: check if the rule contains a subset of the last; if
    // so create a joined selector with the subset, if smaller.
    cache = partialMerge(cache, rule);
  };
}
/**
 * @type {import('postcss').PluginCreator<void>}
 * @return {import('postcss').Plugin}
 */
function pluginCreator() {
  return {
    postcssPlugin: 'postcss-merge-rules',

    prepare(result) {
      /** @type {typeof result.opts & browserslist.Options} */
      const resultOpts = result.opts || {};
      const browsers = browserslist(null, {
        stats: resultOpts.stats,
        path: __dirname,
        env: resultOpts.env,
      });

      const compatibilityCache = new Map();
      return {
        OnceExit(css) {
          css.walkRules(selectorMerger(browsers, compatibilityCache));
        },
      };
    },
  };
}

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


code:
'use strict';
const { isSupported } = require('caniuse-api');
const selectorParser = require('postcss-selector-parser');

const simpleSelectorRe = /^#?[-._a-z0-9 ]+$/i;

const cssSel2 = 'css-sel2';
const cssSel3 = 'css-sel3';
const cssGencontent = 'css-gencontent';
const cssFirstLetter = 'css-first-letter';
const cssFirstLine = 'css-first-line';
const cssInOutOfRange = 'css-in-out-of-range';
const formValidation = 'form-validation';

const vendorPrefix =
  /-(ah|apple|atsc|epub|hp|khtml|moz|ms|o|rim|ro|tc|wap|webkit|xv)-/;

const level2Sel = new Set(['=', '~=', '|=']);
const level3Sel = new Set(['^=', '$=', '*=']);

/**
 * @param {string} selector
 * @return {RegExpMatchArray | null}
 */
function filterPrefixes(selector) {
  return selector.match(vendorPrefix);
}

/**
 * Internet Explorer use :-ms-input-placeholder.
 * Microsoft Edge use ::-ms-input-placeholder.
 *
 * @type {(selector: string) => number}
 */
const findMsInputPlaceholder = (selector) =>
  ~selector.search(/-ms-input-placeholder/i);

/**
 * @param {string[]} selectorsA
 * @param {string[]} selectorsB
 * @return {boolean}
 */
function sameVendor(selectorsA, selectorsB) {
  /** @type {(selectors: string[]) => string} */
  let same = (selectors) => selectors.map(filterPrefixes).join();
  /** @type {(selectors: string[]) => string | undefined} */
  let findMsVendor = (selectors) => selectors.find(findMsInputPlaceholder);
  return (
    same(selectorsA) === same(selectorsB) &&
    !(findMsVendor(selectorsA) && findMsVendor(selectorsB))
  );
}

/**
 * @param {string} selector
 * @return {boolean}
 */
function noVendor(selector) {
  return !vendorPrefix.test(selector);
}

const pseudoElements = {
  ':active': cssSel2,
  ':after': cssGencontent,
  ':any-link': 'css-any-link',
  ':before': cssGencontent,
  ':checked': cssSel3,
  ':default': 'css-default-pseudo',
  ':dir': 'css-dir-pseudo',
  ':disabled': cssSel3,
  ':empty': cssSel3,
  ':enabled': cssSel3,
  ':first-child': cssSel2,
  ':first-letter': cssFirstLetter,
  ':first-line': cssFirstLine,
  ':first-of-type': cssSel3,
  ':focus': cssSel2,
  ':focus-within': 'css-focus-within',
  ':focus-visible': 'css-focus-visible',
  ':has': 'css-has',
  ':hover': cssSel2,
  ':in-range': cssInOutOfRange,
  ':indeterminate': 'css-indeterminate-pseudo',
  ':invalid': formValidation,
  ':is': 'css-matches-pseudo',
  ':lang': cssSel2,
  ':last-child': cssSel3,
  ':last-of-type': cssSel3,
  ':link': cssSel2,
  ':matches': 'css-matches-pseudo',
  ':not': cssSel3,
  ':nth-child': cssSel3,
  ':nth-last-child': cssSel3,
  ':nth-last-of-type': cssSel3,
  ':nth-of-type': cssSel3,
  ':only-child': cssSel3,
  ':only-of-type': cssSel3,
  ':optional': 'css-optional-pseudo',
  ':out-of-range': cssInOutOfRange,
  ':placeholder-shown': 'css-placeholder-shown',
  ':required': formValidation,
  ':root': cssSel3,
  ':target': cssSel3,
  '::after': cssGencontent,
  '::backdrop': 'dialog',
  '::before': cssGencontent,
  '::first-letter': cssFirstLetter,
  '::first-line': cssFirstLine,
  '::marker': 'css-marker-pseudo',
  '::placeholder': 'css-placeholder',
  '::selection': 'css-selection',
  ':valid': formValidation,
  ':visited': cssSel2,
};

/**
 * @param {string} selector
 * @return {boolean}
 */
function isCssMixin(selector) {
  return selector[selector.length - 1] === ':';
}

/**
 * @param {string} selector
 * @return {boolean}
 */
function isHostPseudoClass(selector) {
  return selector.includes(':host');
}

const isSupportedCache = new Map();

// Move to util in future
/**
 * @param {string} feature
 * @param {string[] | undefined} browsers
 */
function isSupportedCached(feature, browsers) {
  const key = JSON.stringify({ feature, browsers });
  let result = isSupportedCache.get(key);

  if (!result) {
    result = isSupported(feature, /** @type {string[]} */ (browsers));
    isSupportedCache.set(key, result);
  }

  return result;
}

/**
 * @param {string[]} selectors
 * @param{string[]=} browsers
 * @param{Map<string,boolean>=} compatibilityCache
 * @return {boolean}
 */
function ensureCompatibility(selectors, browsers, compatibilityCache) {
  // Should not merge mixins
  if (selectors.some(isCssMixin)) {
    return false;
  }

  // Should not merge :host selector https://github.com/angular/angular-cli/issues/18672
  if (selectors.some(isHostPseudoClass)) {
    return false;
  }
  return selectors.every((selector) => {
    if (simpleSelectorRe.test(selector)) {
      return true;
    }
    if (compatibilityCache && compatibilityCache.has(selector)) {
      return compatibilityCache.get(selector);
    }
    let compatible = true;
    selectorParser((ast) => {
      ast.walk((node) => {
        const { type, value } = node;
        if (type === 'pseudo') {
          const entry =
            pseudoElements[/** @type {keyof pseudoElements} */ (value)];
          if (!entry && noVendor(value)) {
            compatible = false;
          }
          if (entry && compatible) {
            compatible = isSupportedCached(entry, browsers);
          }
        }
        if (type === 'combinator') {
          if (value.includes('~')) {
            compatible = isSupportedCached(cssSel3, browsers);
          }
          if (value.includes('>') || value.includes('+')) {
            compatible = isSupportedCached(cssSel2, browsers);
          }
        }
        if (type === 'attribute' && node.attribute) {
          // [foo]
          if (!node.operator) {
            compatible = isSupportedCached(cssSel2, browsers);
          }
          if (value) {
            // [foo="bar"], [foo~="bar"], [foo|="bar"]
            if (level2Sel.has(/** @type {string} */ (node.operator))) {
              compatible = isSupportedCached(cssSel2, browsers);
            }
            // [foo^="bar"], [foo$="bar"], [foo*="bar"]
            if (level3Sel.has(/** @type {string} */ (node.operator))) {
              compatible = isSupportedCached(cssSel3, browsers);
            }
          }

          // [foo="bar" i]
          if (node.insensitive) {
            compatible = isSupportedCached('css-case-insensitive', browsers);
          }
        }
        if (!compatible) {
          // If this node was not compatible,
          // break out early from walking the rest
          return false;
        }
      });
    }).processSync(selector);
    if (compatibilityCache) {
      compatibilityCache.set(selector, compatible);
    }
    return compatible;
  });
}

module.exports = { sameVendor, noVendor, pseudoElements, ensureCompatibility };


code:
export = pluginCreator;
/**
 * @type {import('postcss').PluginCreator<void>}
 * @return {import('postcss').Plugin}
 */
declare function pluginCreator(): import('postcss').Plugin;
declare namespace pluginCreator {
    const postcss: true;
}


code:
/**
 * @param {string[]} selectorsA
 * @param {string[]} selectorsB
 * @return {boolean}
 */
export function sameVendor(selectorsA: string[], selectorsB: string[]): boolean;
/**
 * @param {string} selector
 * @return {boolean}
 */
export function noVendor(selector: string): boolean;
export const pseudoElements: {
    ':active': string;
    ':after': string;
    ':any-link': string;
    ':before': string;
    ':checked': string;
    ':default': string;
    ':dir': string;
    ':disabled': string;
    ':empty': string;
    ':enabled': string;
    ':first-child': string;
    ':first-letter': string;
    ':first-line': string;
    ':first-of-type': string;
    ':focus': string;
    ':focus-within': string;
    ':focus-visible': string;
    ':has': string;
    ':hover': string;
    ':in-range': string;
    ':indeterminate': string;
    ':invalid': string;
    ':is': string;
    ':lang': string;
    ':last-child': string;
    ':last-of-type': string;
    ':link': string;
    ':matches': string;
    ':not': string;
    ':nth-child': string;
    ':nth-last-child': string;
    ':nth-last-of-type': string;
    ':nth-of-type': string;
    ':only-child': string;
    ':only-of-type': string;
    ':optional': string;
    ':out-of-range': string;
    ':placeholder-shown': string;
    ':required': string;
    ':root': string;
    ':target': string;
    '::after': string;
    '::backdrop': string;
    '::before': string;
    '::first-letter': string;
    '::first-line': string;
    '::marker': string;
    '::placeholder': string;
    '::selection': string;
    ':valid': string;
    ':visited': string;
};
/**
 * @param {string[]} selectors
 * @param{string[]=} browsers
 * @param{Map<string,boolean>=} compatibilityCache
 * @return {boolean}
 */
export function ensureCompatibility(selectors: string[], browsers?: string[] | undefined, compatibilityCache?: Map<string, boolean> | undefined): boolean;


code:
# postcss-minify-font-values [![Build Status][ci-img]][ci]

> Minify font declarations with PostCSS.

This module will try to minimise the `font-family`, `font-weight` and `font` shorthand
properties; it can unquote font families where necessary, detect & remove
duplicates, and cut short a declaration after it finds a keyword. For more
examples, see the [tests](test).

```css
h1 {
  font:bold 2.2rem/.9 "Open Sans Condensed", sans-serif;
}

p {
  font-family: "Helvetica Neue", Arial, sans-serif, Helvetica;
  font-weight: normal;
}
```

```css
h1 {
  font:700 2.2rem/.9 Open Sans Condensed,sans-serif
}

p {
  font-family: Helvetica Neue,Arial,sans-serif;
  font-weight: 400;
}
```

## API

### minifyFontValues([options])

#### options

##### removeAfterKeyword

Type: `boolean`
Default: `false`

Pass `true` to remove font families after the module encounters a font keyword,
for example `sans-serif`.

##### removeDuplicates

Type: `boolean`
Default: `true`

Pass `false` to disable the module from removing duplicated font families.

##### removeQuotes

Type: `boolean`
Default: `true`

Pass `false` to disable the module from removing quotes from font families.
Note that oftentimes, this is a *safe optimisation* & is done safely. For more
details, see [Mathias Bynens' article][mathias].

## Usage

```js
postcss([ require('postcss-minify-font-values') ])
```

See [PostCSS] docs for examples for your environment.

## Contributors

See [CONTRIBUTORS.md](https://github.com/cssnano/cssnano/blob/master/CONTRIBUTORS.md).

# License

MIT © [Bogdan Chadkin](mailto:trysound@yandex.ru)

[mathias]: https://mathiasbynens.be/notes/unquoted-font-family
[PostCSS]: https://github.com/postcss/postcss
[ci-img]:  https://travis-ci.org/cssnano/postcss-minify-font-values.svg
[ci]:      https://travis-ci.org/cssnano/postcss-minify-font-values


code:
'use strict';
const valueParser = require('postcss-value-parser');
const minifyWeight = require('./lib/minify-weight');
const minifyFamily = require('./lib/minify-family');
const minifyFont = require('./lib/minify-font');

/**
 * @param {string} value
 * @return {boolean}
 */
function hasVariableFunction(value) {
  const lowerCasedValue = value.toLowerCase();

  return lowerCasedValue.includes('var(') || lowerCasedValue.includes('env(');
}

/**
 * @param {string} prop
 * @param {string} value
 * @param {Options} opts
 * @return {string}
 */
function transform(prop, value, opts) {
  let lowerCasedProp = prop.toLowerCase();

  if (lowerCasedProp === 'font-weight' && !hasVariableFunction(value)) {
    return minifyWeight(value);
  } else if (lowerCasedProp === 'font-family' && !hasVariableFunction(value)) {
    const tree = valueParser(value);

    tree.nodes = minifyFamily(tree.nodes, opts);

    return tree.toString();
  } else if (lowerCasedProp === 'font') {
    const tree = valueParser(value);

    tree.nodes = minifyFont(tree.nodes, opts);

    return tree.toString();
  }

  return value;
}

/** @typedef {{removeAfterKeyword?: boolean, removeDuplicates?: boolean, removeQuotes?: boolean}} Options */

/**
 * @type {import('postcss').PluginCreator<Options>}
 * @param {Options} opts
 * @return {import('postcss').Plugin}
 */
function pluginCreator(opts) {
  opts = Object.assign(
    {},
    {
      removeAfterKeyword: false,
      removeDuplicates: true,
      removeQuotes: true,
    },
    opts
  );

  return {
    postcssPlugin: 'postcss-minify-font-values',
    prepare() {
      const cache = new Map();
      return {
        OnceExit(css) {
          css.walkDecls(/font/i, (decl) => {
            const value = decl.value;

            if (!value) {
              return;
            }

            const prop = decl.prop;

            const cacheKey = `${prop}|${value}`;

            if (cache.has(cacheKey)) {
              decl.value = cache.get(cacheKey);

              return;
            }

            const newValue = transform(prop, value, opts);

            decl.value = newValue;
            cache.set(cacheKey, newValue);
          });
        },
      };
    },
  };
}

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


code:
'use strict';
module.exports = {
  style: new Set(['italic', 'oblique']),
  variant: new Set(['small-caps']),
  weight: new Set([
    '100',
    '200',
    '300',
    '400',
    '500',
    '600',
    '700',
    '800',
    '900',
    'bold',
    'lighter',
    'bolder',
  ]),
  stretch: new Set([
    'ultra-condensed',
    'extra-condensed',
    'condensed',
    'semi-condensed',
    'semi-expanded',
    'expanded',
    'extra-expanded',
    'ultra-expanded',
  ]),
  size: new Set([
    'xx-small',
    'x-small',
    'small',
    'medium',
    'large',
    'x-large',
    'xx-large',
    'larger',
    'smaller',
  ]),
};


code:
'use strict';
const { stringify } = require('postcss-value-parser');

/**
 * @param {string[]} list
 * @return {string[]}
 */
function uniqueFontFamilies(list) {
  return list.filter((item, i) => {
    if (item.toLowerCase() === 'monospace') {
      return true;
    }
    return i === list.indexOf(item);
  });
}

const globalKeywords = ['inherit', 'initial', 'unset'];
const genericFontFamilykeywords = new Set([
  'sans-serif',
  'serif',
  'fantasy',
  'cursive',
  'monospace',
  'system-ui',
]);

/**
 * @param {string} value
 * @param {number} length
 * @return {string[]}
 */
function makeArray(value, length) {
  let array = [];
  while (length--) {
    array[length] = value;
  }
  return array;
}

// eslint-disable-next-line no-useless-escape
const regexSimpleEscapeCharacters = /[ !"#$%&'()*+,.\/;<=>?@\[\\\]^`{|}~]/;

/**
 * @param {string} string
 * @param {boolean} escapeForString
 * @return {string}
 */
function escape(string, escapeForString) {
  let counter = 0;
  let character;
  let charCode;
  let value;
  let output = '';

  while (counter < string.length) {
    character = string.charAt(counter++);
    charCode = character.charCodeAt(0);

    // \r is already tokenized away at this point
    // `:` can be escaped as `\:`, but that fails in IE < 8
    if (!escapeForString && /[\t\n\v\f:]/.test(character)) {
      value = '\\' + charCode.toString(16) + ' ';
    } else if (
      !escapeForString &&
      regexSimpleEscapeCharacters.test(character)
    ) {
      value = '\\' + character;
    } else {
      value = character;
    }

    output += value;
  }

  if (!escapeForString) {
    if (/^-[-\d]/.test(output)) {
      output = '\\-' + output.slice(1);
    }

    const firstChar = string.charAt(0);

    if (/\d/.test(firstChar)) {
      output = '\\3' + firstChar + ' ' + output.slice(1);
    }
  }

  return output;
}

const regexKeyword = new RegExp(
  [...genericFontFamilykeywords].concat(globalKeywords).join('|'),
  'i'
);
const regexInvalidIdentifier = /^(-?\d|--)/;
const regexSpaceAtStart = /^\x20/;
const regexWhitespace = /[\t\n\f\r\x20]/g;
const regexIdentifierCharacter = /^[a-zA-Z\d\xa0-\uffff_-]+$/;
const regexConsecutiveSpaces = /(\\(?:[a-fA-F0-9]{1,6}\x20|\x20))?(\x20{2,})/g;
const regexTrailingEscape = /\\[a-fA-F0-9]{0,6}\x20$/;
const regexTrailingSpace = /\x20$/;

/**
 * @param {string} string
 * @return {string}
 */
function escapeIdentifierSequence(string) {
  let identifiers = string.split(regexWhitespace);
  let index = 0;
  /** @type {string[] | string} */
  let result = [];
  let escapeResult;

  while (index < identifiers.length) {
    let subString = identifiers[index++];

    if (subString === '') {
      result.push(subString);
      continue;
    }

    escapeResult = escape(subString, false);

    if (regexIdentifierCharacter.test(subString)) {
      // the font family name part consists of allowed characters exclusively
      if (regexInvalidIdentifier.test(subString)) {
        // the font family name part starts with two hyphens, a digit, or a
        // hyphen followed by a digit
        if (index === 1) {
          // if this is the first item
          result.push(escapeResult);
        } else {
          // if it’s not the first item, we can simply escape the space
          // between the two identifiers to merge them into a single
          // identifier rather than escaping the start characters of the
          // second identifier
          result[index - 2] += '\\';
          result.push(escape(subString, true));
        }
      } else {
        // the font family name part doesn’t start with two hyphens, a digit,
        // or a hyphen followed by a digit
        result.push(escapeResult);
      }
    } else {
      // the font family name part contains invalid identifier characters
      result.push(escapeResult);
    }
  }

  result = result.join(' ').replace(regexConsecutiveSpaces, ($0, $1, $2) => {
    const spaceCount = $2.length;
    const escapesNeeded = Math.floor(spaceCount / 2);
    const array = makeArray('\\ ', escapesNeeded);

    if (spaceCount % 2) {
      array[escapesNeeded - 1] += '\\ ';
    }

    return ($1 || '') + ' ' + array.join(' ');
  });

  // Escape trailing spaces unless they’re already part of an escape
  if (regexTrailingSpace.test(result) && !regexTrailingEscape.test(result)) {
    result = result.replace(regexTrailingSpace, '\\ ');
  }

  if (regexSpaceAtStart.test(result)) {
    result = '\\ ' + result.slice(1);
  }

  return result;
}
/**
 * @param {import('postcss-value-parser').Node[]} nodes
 * @param {import('../index').Options} opts
 * @return {import('postcss-value-parser').WordNode[]}
 */
module.exports = function (nodes, opts) {
  /** @type {import('postcss-value-parser').Node[]} */
  const family = [];
  /** @type {import('postcss-value-parser').WordNode | null} */
  let last = null;
  let i, max;

  nodes.forEach((node, index, arr) => {
    if (node.type === 'string' || node.type === 'function') {
      family.push(node);
    } else if (node.type === 'word') {
      if (!last) {
        last = /** @type {import('postcss-value-parser').WordNode} */ ({
          type: 'word',
          value: '',
        });
        family.push(last);
      }

      last.value += node.value;
    } else if (node.type === 'space') {
      if (last && index !== arr.length - 1) {
        last.value += ' ';
      }
    } else {
      last = null;
    }
  });

  let normalizedFamilies = family.map((node) => {
    if (node.type === 'string') {
      const isKeyword = regexKeyword.test(node.value);

      if (
        !opts.removeQuotes ||
        isKeyword ||
        /[0-9]/.test(node.value.slice(0, 1))
      ) {
        return stringify(node);
      }

      let escaped = escapeIdentifierSequence(node.value);

      if (escaped.length < node.value.length + 2) {
        return escaped;
      }
    }

    return stringify(node);
  });

  if (opts.removeAfterKeyword) {
    for (i = 0, max = normalizedFamilies.length; i < max; i += 1) {
      if (genericFontFamilykeywords.has(normalizedFamilies[i].toLowerCase())) {
        normalizedFamilies = normalizedFamilies.slice(0, i + 1);
        break;
      }
    }
  }

  if (opts.removeDuplicates) {
    normalizedFamilies = uniqueFontFamilies(normalizedFamilies);
  }

  return [
    /** @type {import('postcss-value-parser').WordNode} */ ({
      type: 'word',
      value: normalizedFamilies.join(),
    }),
  ];
};


code:
'use strict';
const { unit } = require('postcss-value-parser');
const keywords = require('./keywords');
const minifyFamily = require('./minify-family');
const minifyWeight = require('./minify-weight');

/**
 * @param {import('postcss-value-parser').Node[]} nodes
 * @param {import('../index').Options} opts
 * @return {import('postcss-value-parser').Node[]}
 */
module.exports = function (nodes, opts) {
  let i, max, node, family;
  let familyStart = NaN;
  let hasSize = false;

  for (i = 0, max = nodes.length; i < max; i += 1) {
    node = nodes[i];

    if (node.type === 'word') {
      if (hasSize) {
        continue;
      }

      const value = node.value.toLowerCase();

      if (
        value === 'normal' ||
        value === 'inherit' ||
        value === 'initial' ||
        value === 'unset'
      ) {
        familyStart = i;
      } else if (keywords.style.has(value) || unit(value)) {
        familyStart = i;
      } else if (keywords.variant.has(value)) {
        familyStart = i;
      } else if (keywords.weight.has(value)) {
        node.value = minifyWeight(value);
        familyStart = i;
      } else if (keywords.stretch.has(value)) {
        familyStart = i;
      } else if (keywords.size.has(value) || unit(value)) {
        familyStart = i;
        hasSize = true;
      }
    } else if (
      node.type === 'function' &&
      nodes[i + 1] &&
      nodes[i + 1].type === 'space'
    ) {
      familyStart = i;
    } else if (node.type === 'div' && node.value === '/') {
      familyStart = i + 1;
      break;
    }
  }

  familyStart += 2;

  family = minifyFamily(nodes.slice(familyStart), opts);

  return nodes.slice(0, familyStart).concat(family);
};


code:
'use strict';
/**
 * @param {string} value
 * @return {string}
 */
module.exports = function (value) {
  const lowerCasedValue = value.toLowerCase();

  return lowerCasedValue === 'normal'
    ? '400'
    : lowerCasedValue === 'bold'
    ? '700'
    : value;
};


code:
export = pluginCreator;
/** @typedef {{removeAfterKeyword?: boolean, removeDuplicates?: boolean, removeQuotes?: boolean}} Options */
/**
 * @type {import('postcss').PluginCreator<Options>}
 * @param {Options} opts
 * @return {import('postcss').Plugin}
 */
declare function pluginCreator(opts: Options): import('postcss').Plugin;
declare namespace pluginCreator {
    export { postcss, Options };
}
type Options = {
    removeAfterKeyword?: boolean;
    removeDuplicates?: boolean;
    removeQuotes?: boolean;
};
declare var postcss: true;


code:
export const style: Set<string>;
export const variant: Set<string>;
export const weight: Set<string>;
export const stretch: Set<string>;
export const size: Set<string>;


code:
declare function _exports(nodes: import('postcss-value-parser').Node[], opts: import('../index').Options): import('postcss-value-parser').WordNode[];
export = _exports;


code:
declare function _exports(nodes: import('postcss-value-parser').Node[], opts: import('../index').Options): import('postcss-value-parser').Node[];
export = _exports;


code:
declare function _exports(value: string): string;
export = _exports;


code:
Copyright (c) Ben Briggs <beneb.info@gmail.com> (http://beneb.info)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.


code:
# [postcss][postcss]-minify-gradients

> Minify gradient parameters with PostCSS.

## Install

With [npm](https://npmjs.org/package/postcss-minify-gradients) do:

```
npm install postcss-minify-gradients
```

## Example

Where possible, this module will minify gradient parameters. It can convert
linear gradient directional syntax to angles, remove the unnecessary `0%` and
`100%` start and end values, and minimise color stops that use the same length
values (the browser will adjust the value automatically).

### Input

```css
h1 {
    background: linear-gradient(to bottom, #ffe500 0%, #ffe500 50%, #121 50%, #121 100%)
}
```

### Output

```css
h1 {
    background: linear-gradient(180deg, #ffe500, #ffe500 50%, #121 0, #121)
}
```

## Usage

See the [PostCSS documentation](https://github.com/postcss/postcss#usage) for
examples for your environment.

## Contributors

See [CONTRIBUTORS.md](https://github.com/cssnano/cssnano/blob/master/CONTRIBUTORS.md).

## License

MIT © [Ben Briggs](http://beneb.info)

[postcss]: https://github.com/postcss/postcss


code:
'use strict';
const valueParser = require('postcss-value-parser');
const { getArguments } = require('cssnano-utils');
const isColorStop = require('./isColorStop.js');

const angles = {
  top: '0deg',
  right: '90deg',
  bottom: '180deg',
  left: '270deg',
};

/**
 * @param {valueParser.Dimension} a
 * @param {valueParser.Dimension} b
 * @return {boolean}
 */
function isLessThan(a, b) {
  return (
    a.unit.toLowerCase() === b.unit.toLowerCase() &&
    parseFloat(a.number) >= parseFloat(b.number)
  );
}
/**
 * @param {import('postcss').Declaration} decl
 * @return {void}
 */
function optimise(decl) {
  const value = decl.value;

  if (!value) {
    return;
  }

  const normalizedValue = value.toLowerCase();

  if (normalizedValue.includes('var(') || normalizedValue.includes('env(')) {
    return;
  }

  if (!normalizedValue.includes('gradient')) {
    return;
  }

  decl.value = valueParser(value)
    .walk((node) => {
      if (node.type !== 'function' || !node.nodes.length) {
        return false;
      }

      const lowerCasedValue = node.value.toLowerCase();

      if (
        lowerCasedValue === 'linear-gradient' ||
        lowerCasedValue === 'repeating-linear-gradient' ||
        lowerCasedValue === '-webkit-linear-gradient' ||
        lowerCasedValue === '-webkit-repeating-linear-gradient'
      ) {
        let args = getArguments(node);

        if (
          node.nodes[0].value.toLowerCase() === 'to' &&
          args[0].length === 3
        ) {
          node.nodes = node.nodes.slice(2);
          node.nodes[0].value =
            angles[
              /** @type {'top'|'right'|'bottom'|'left'}*/ (
                node.nodes[0].value.toLowerCase()
              )
            ];
        }

        /** @type {valueParser.Dimension | false} */
        let lastStop;

        args.forEach((arg, index) => {
          if (arg.length !== 3) {
            return;
          }

          let isFinalStop = index === args.length - 1;
          let thisStop = valueParser.unit(arg[2].value);

          if (lastStop === undefined) {
            lastStop = thisStop;

            if (
              !isFinalStop &&
              lastStop &&
              lastStop.number === '0' &&
              lastStop.unit.toLowerCase() !== 'deg'
            ) {
              arg[1].value = arg[2].value = '';
            }

            return;
          }

          if (lastStop && thisStop && isLessThan(lastStop, thisStop)) {
            arg[2].value = '0';
          }

          lastStop = thisStop;

          if (isFinalStop && arg[2].value === '100%') {
            arg[1].value = arg[2].value = '';
          }
        });

        return false;
      }

      if (
        lowerCasedValue === 'radial-gradient' ||
        lowerCasedValue === 'repeating-radial-gradient'
      ) {
        let args = getArguments(node);
        /** @type {valueParser.Dimension | false} */
        let lastStop;

        const hasAt = args[0].find((n) => n.value.toLowerCase() === 'at');

        args.forEach((arg, index) => {
          if (!arg[2] || (!index && hasAt)) {
            return;
          }

          let thisStop = valueParser.unit(arg[2].value);

          if (!lastStop) {
            lastStop = thisStop;

            return;
          }

          if (lastStop && thisStop && isLessThan(lastStop, thisStop)) {
            arg[2].value = '0';
          }

          lastStop = thisStop;
        });

        return false;
      }

      if (
        lowerCasedValue === '-webkit-radial-gradient' ||
        lowerCasedValue === '-webkit-repeating-radial-gradient'
      ) {
        let args = getArguments(node);
        /** @type {valueParser.Dimension | false} */
        let lastStop;

        args.forEach((arg) => {
          let color;
          let stop;

          if (arg[2] !== undefined) {
            if (arg[0].type === 'function') {
              color = `${arg[0].value}(${valueParser.stringify(arg[0].nodes)})`;
            } else {
              color = arg[0].value;
            }

            if (arg[2].type === 'function') {
              stop = `${arg[2].value}(${valueParser.stringify(arg[2].nodes)})`;
            } else {
              stop = arg[2].value;
            }
          } else {
            if (arg[0].type === 'function') {
              color = `${arg[0].value}(${valueParser.stringify(arg[0].nodes)})`;
            }

            color = arg[0].value;
          }

          color = color.toLowerCase();

          const colorStop =
            stop !== undefined
              ? isColorStop(color, stop.toLowerCase())
              : isColorStop(color);

          if (!colorStop || !arg[2]) {
            return;
          }

          let thisStop = valueParser.unit(arg[2].value);

          if (!lastStop) {
            lastStop = thisStop;

            return;
          }

          if (lastStop && thisStop && isLessThan(lastStop, thisStop)) {
            arg[2].value = '0';
          }

          lastStop = thisStop;
        });

        return false;
      }
    })
    .toString();
}
/**
 * @type {import('postcss').PluginCreator<void>}
 * @return {import('postcss').Plugin}
 */
function pluginCreator() {
  return {
    postcssPlugin: 'postcss-minify-gradients',
    OnceExit(css) {
      css.walkDecls(optimise);
    },
  };
}

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


code:
'use strict';
const { unit } = require('postcss-value-parser');
const { colord, extend } = require('colord');
const namesPlugin = require('colord/plugins/names');

extend([/** @type {any} */ (namesPlugin)]);

/* Code derived from https://github.com/pigcan/is-color-stop */

const lengthUnits = new Set([
  'PX',
  'IN',
  'CM',
  'MM',
  'EM',
  'REM',
  'POINTS',
  'PC',
  'EX',
  'CH',
  'VW',
  'VH',
  'VMIN',
  'VMAX',
  '%',
]);

/**
 * @param {string} input
 * @return {boolean}
 */
function isCSSLengthUnit(input) {
  return lengthUnits.has(input.toUpperCase());
}
/**
 * @param {string|undefined} str
 * @return {boolean}
 */
function isStop(str) {
  if (str) {
    let stop = false;
    const node = unit(str);
    if (node) {
      const number = Number(node.number);
      if (number === 0 || (!isNaN(number) && isCSSLengthUnit(node.unit))) {
        stop = true;
      }
    } else {
      stop = /^calc\(\S+\)$/g.test(str);
    }
    return stop;
  }
  return true;
}
/**
 * @param {string} color
 * @param {string} [stop]
 * @return {boolean}
 */
module.exports = function isColorStop(color, stop) {
  return colord(color).isValid() && isStop(stop);
};


code:
export = pluginCreator;
/**
 * @type {import('postcss').PluginCreator<void>}
 * @return {import('postcss').Plugin}
 */
declare function pluginCreator(): import('postcss').Plugin;
declare namespace pluginCreator {
    const postcss: true;
}


code:
declare function _exports(color: string, stop?: string | undefined): boolean;
export = _exports;


code:
# postcss-minify-params [![Build Status][ci-img]][ci]

> Minify at-rule params with PostCSS.

```css
@media only screen   and ( min-width: 400px, min-height: 500px ) {
    h2{
        color:blue
    }
}
```

```css
@media only screen and (min-width:400px,min-height:500px) {
    h2{
        color:blue
    }
}
```

## Usage

```js
postcss([ require('postcss-minify-params') ])
```

See [PostCSS] docs for examples for your environment.

## Contributors

See [CONTRIBUTORS.md](https://github.com/cssnano/cssnano/blob/master/CONTRIBUTORS.md).

## License

MIT © [Bogdan Chadkin](mailto:trysound@yandex.ru)

[PostCSS]: https://github.com/postcss/postcss
[ci-img]:  https://travis-ci.org/cssnano/postcss-minify-params.svg
[ci]:      https://travis-ci.org/cssnano/postcss-minify-params


postcss-minify-params

description: Minify at-rule params with PostCSS

keywords: postcss css postcss-plugin minify optimise params

files: src LICENSE types

author: Bogdan Chadkin <trysound@yandex.ru>


code:
'use strict';
const browserslist = require('browserslist');
const valueParser = require('postcss-value-parser');
const { getArguments } = require('cssnano-utils');

/**
 * Return the greatest common divisor
 * of two numbers.
 *
 * @param {number} a
 * @param {number} b
 * @return {number}
 */
function gcd(a, b) {
  return b ? gcd(b, a % b) : a;
}

/**
 * @param {number} a
 * @param {number} b
 * @return {[number, number]}
 */
function aspectRatio(a, b) {
  const divisor = gcd(a, b);

  return [a / divisor, b / divisor];
}

/**
 * @param {valueParser.Node[]} args
 * @return {string}
 */
function split(args) {
  return args.map((arg) => valueParser.stringify(arg)).join('');
}

/**
 * @param {valueParser.Node} node
 * @return {void}
 */
function removeNode(node) {
  node.value = '';
  node.type = 'word';
}

/**
 * @param {unknown[]} items
 * @return {string}
 */
function sortAndDedupe(items) {
  const a = [...new Set(items)];
  a.sort();
  return a.join();
}

/**
 * @param {boolean} legacy
 * @param {import('postcss').AtRule} rule
 * @return {void}
 */
function transform(legacy, rule) {
  const ruleName = rule.name.toLowerCase();

  // We should re-arrange parameters only for `@media` and `@supports` at-rules
  if (!rule.params || !['media', 'supports'].includes(ruleName)) {
    return;
  }

  const params = valueParser(rule.params);

  params.walk((node, index) => {
    if (node.type === 'div') {
      node.before = node.after = '';
    } else if (node.type === 'function') {
      node.before = '';
      if (
        node.nodes[0] &&
        node.nodes[0].type === 'word' &&
        node.nodes[0].value.startsWith('--') &&
        node.nodes[2] === undefined
      ) {
        node.after = ' ';
      } else {
        node.after = '';
      }
      if (
        node.nodes[4] &&
        node.nodes[0].value.toLowerCase().indexOf('-aspect-ratio') === 3
      ) {
        const [a, b] = aspectRatio(
          Number(node.nodes[2].value),
          Number(node.nodes[4].value)
        );

        node.nodes[2].value = a.toString();
        node.nodes[4].value = b.toString();
      }
    } else if (node.type === 'space') {
      node.value = ' ';
    } else {
      const prevWord = params.nodes[index - 2];

      if (
        node.value.toLowerCase() === 'all' &&
        rule.name.toLowerCase() === 'media' &&
        !prevWord
      ) {
        const nextWord = params.nodes[index + 2];

        if (!legacy || nextWord) {
          removeNode(node);
        }

        if (nextWord && nextWord.value.toLowerCase() === 'and') {
          const nextSpace = params.nodes[index + 1];
          const secondSpace = params.nodes[index + 3];

          removeNode(nextWord);
          removeNode(nextSpace);
          removeNode(secondSpace);
        }
      }
    }
  }, true);

  rule.params = sortAndDedupe(getArguments(params).map(split));

  if (!rule.params.length) {
    rule.raws.afterName = '';
  }
}

const allBugBrowers = new Set(['ie 10', 'ie 11']);

/**
 * @type {import('postcss').PluginCreator<browserslist.Options>}
 * @param {browserslist.Options} options
 * @return {import('postcss').Plugin}
 */
function pluginCreator(options = {}) {
  const browsers = browserslist(null, {
    stats: options.stats,
    path: __dirname,
    env: options.env,
  });

  const hasAllBug = browsers.some((browser) => allBugBrowers.has(browser));
  return {
    postcssPlugin: 'postcss-minify-params',

    OnceExit(css) {
      css.walkAtRules((rule) => transform(hasAllBug, rule));
    },
  };
}

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


code:
export = pluginCreator;
/**
 * @type {import('postcss').PluginCreator<browserslist.Options>}
 * @param {browserslist.Options} options
 * @return {import('postcss').Plugin}
 */
declare function pluginCreator(options?: browserslist.Options): import('postcss').Plugin;
declare namespace pluginCreator {
    const postcss: true;
}
import browserslist = require("browserslist");


code:
# [postcss][postcss]-minify-selectors

> Minify selectors with PostCSS.

## Install

With [npm](https://www.npmjs.com/package/postcss-minify-selectors) do:

```
npm install postcss-minify-selectors --save
```

## Example

### Input

```css
h1 + p, h2, h3, h2{color:blue}
```

### Output

```css
h1+p,h2,h3{color:blue}
```

For more examples see the [tests](test.js).

## Usage

See the [PostCSS documentation](https://github.com/postcss/postcss#usage) for
examples for your environment.

## Contributors

See [CONTRIBUTORS.md](https://github.com/cssnano/cssnano/blob/master/CONTRIBUTORS.md).

## License

MIT © [Ben Briggs](http://beneb.info)

[postcss]: https://github.com/postcss/postcss


postcss-minify-selectors

description: Minify selectors with PostCSS.

files: src LICENSE-MIT types

keywords: css minify optimise postcss postcss-plugin selectors