UNPKG

cssnano

Version:

A modular CSS minifier, built on top of the PostCSS ecosystem.

119 lines (103 loc) • 2.78 kB
const OVERRIDABLE_RULES = new Set(['keyframes', 'counter-style']); const SCOPE_RULES = new Set(['media', 'supports', 'container', 'layer']); const vendorPrefixPattern = /^-\w+-/v; /** * @param {string} prop * @return {string} */ function vendorUnprefixed(prop) { return prop.replace(vendorPrefixPattern, ''); } /** * @param {string} name * @return {boolean} */ function isOverridable(name) { return OVERRIDABLE_RULES.has(vendorUnprefixed(name.toLowerCase())); } /** * @param {string} name * @return {boolean} */ function isScope(name) { return SCOPE_RULES.has(vendorUnprefixed(name.toLowerCase())); } /** * @param {import('postcss').AtRule} node * @return {string} */ function getScope(node) { /** @type {import('postcss').Container<import('postcss').ChildNode> | import('postcss').Document | undefined} */ let current = node.parent; const chain = [node.name.toLowerCase(), node.params]; while (current) { if ( current.type === 'atrule' && isScope(/** @type import('postcss').AtRule */ (current).name) ) { const atRule = /** @type import('postcss').AtRule */ (current); chain.unshift( atRule.params ? atRule.name.toLowerCase() + ' ' + atRule.params : atRule.name.toLowerCase() ); } current = current.parent; } return chain.join('|'); } /** * Traverses container nodes from bottom to top so the winning at-rule * in any scope is visited first. Earlier duplicate scopes are removed. * * @param {import('postcss').Container<import('postcss').ChildNode> | import('postcss').Document} container * @param {Set<string>} seen * @return {void} */ function walkBackward(container, seen) { const { nodes } = container; if (!nodes) { return; } for (let i = nodes.length - 1; i >= 0; i--) { const child = nodes[i]; if (child.type === 'atrule') { if (isOverridable(child.name)) { const scope = getScope(child); if (seen.has(scope)) { child.remove(); } else { seen.add(scope); } continue; } } if (/** @type {import('postcss').Container} */ (child).nodes) { walkBackward( /** @type {import('postcss').Container<import('postcss').ChildNode>} */ ( child ), seen ); } } } /** * @return {import('postcss').Plugin} */ function pluginCreator() { return { postcssPlugin: 'postcss-discard-overridden', /** * @param {import('postcss').Root} css */ OnceExit(css) { const seen = new Set(); walkBackward(css, seen); }, }; } /** @type {true} */ pluginCreator.postcss = true; const moduleExports = pluginCreator; export { moduleExports as default, moduleExports as 'module.exports' };