Skip to content

feat: add option to only remove exact duplicate property/values #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,40 @@ will combine into
}
```

In order to limit this to only combining properties when the values are equal, set the `removeDuplicatedValues` option to `true` instead. This could clean up duplicated properties, but allow for conscious duplicates such as fallbacks for custom properties.

```js
const postcss = require('postcss');
const combineSelectors = require('postcss-combine-duplicated-selectors');

postcss([combineSelectors({removeDuplicatedValues: true})]);
```

This will transform the following css

```css
.a {
height: 10px;
}

.a {
width: 20px;
background: var(--custom-color);
background: rgba(255, 165, 0, 0.5);
}
```

into

```css
.a {
height: 10px;
width: 20px;
background: var(--custom-color);
background: rgba(255, 165, 0, 0.5);
}
```

### Media Queries

If you have code with media queries, pass code through [_postcss-combine-media-query_](https://github.com/SassNinja/postcss-combine-media-query) or [_css-mquery-packer_](https://github.com/n19htz/css-mquery-packer) before _postcss-combine-duplicated-selectors_ to ensure optimal results.
33 changes: 24 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,21 @@ function sortGroups(selector) {
/**
* Remove duplicated properties
* @param {Object} selector - postcss selector node
* @param {Boolean} exact
*/
function removeDupProperties(selector) {
function removeDupProperties(selector, exact) {
// Remove duplicated properties from bottom to top ()
for (let actIndex = selector.nodes.length - 1; actIndex >= 1; actIndex--) {
for (let befIndex = actIndex - 1; befIndex >= 0; befIndex--) {
if (selector.nodes[actIndex].prop === selector.nodes[befIndex].prop) {
selector.nodes[befIndex].remove();
actIndex--;
if (
!exact ||
(exact &&
selector.nodes[actIndex].value === selector.nodes[befIndex].value)
) {
selector.nodes[befIndex].remove();
actIndex--;
}
}
}
}
Expand Down Expand Up @@ -79,8 +86,8 @@ module.exports = postcss.plugin(name, (options) => {

// See if this query key is already in the map table
map = mapTable.has(query) ? // If it is use it
mapTable.get(query) : // if not set it and get it
mapTable.set(query, new Map()).get(query);
mapTable.get(query) : // if not set it and get it
mapTable.set(query, new Map()).get(query);
} else {
// Otherwise we are dealing with a selector in the root
map = mapTable.get('root');
Expand All @@ -100,12 +107,20 @@ module.exports = postcss.plugin(name, (options) => {
// remove duplicated rule
rule.remove();

if (options.removeDuplicatedProperties) {
removeDupProperties(destination);
if (options.removeDuplicatedProperties ||
options.removeDuplicatedValues) {
removeDupProperties(
destination,
options.removeDuplicatedValues,
);
}
} else {
if (options.removeDuplicatedProperties) {
removeDupProperties(rule);
if (options.removeDuplicatedProperties ||
options.removeDuplicatedValues) {
removeDupProperties(
rule,
options.removeDuplicatedValues,
);
}
// add new selector to symbol table
map.set(selector, rule);
Expand Down
59 changes: 56 additions & 3 deletions test/duplicated-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const plugin = require('../src');
*/

/**
* Take string literals are remove newlines and extra spacing so results print
* Take string literals and remove newlines and extra spacing so results print
* as expected in logs
* @return {string} string without newlines and tabs
*/
Expand All @@ -23,7 +23,7 @@ const removeDuplicates = testFactory(
);

test(
'remove duplicated properties when combine selectors',
'remove duplicated properties when combining selectors',
removeDuplicates,
'.a {height: 10px; color: black;} .a {color: blue; width: 20px;}',
'.a {height: 10px;color: blue; width: 20px;}',
Expand Down Expand Up @@ -54,7 +54,7 @@ const keepDuplicates = testFactory(
);

test(
'maintain duplicated properties when combine selectors',
'maintain duplicated properties when combining selectors',
keepDuplicates,
'.a {height: 10px; color: black;} .a {color: blue; width: 20px;}',
'.a {height: 10px; color: black;color: blue; width: 20px;}',
Expand All @@ -78,3 +78,56 @@ test(
}
`,
);

// Only duplicated properties with matching values should be removed
const removeExactDuplicates = testFactory(
'css',
[plugin({removeDuplicatedValues: true})],
);

test(
'remove duplicated properties with matching values with combined selectors',
removeExactDuplicates,
'.a {height: 10px; color: red;} .a {color: red; color: blue; width: 20px;}',
'.a {height: 10px;color: red; color: blue; width: 20px;}',
);

test(
'remove duplicated properties with matching values in a selector',
removeExactDuplicates,
minify`
.a {
height: 10px;
background: orange;
background: orange;
background: rgba(255, 165, 0, 0.5);
}
`,
minify`
.a {
height: 10px;
background: orange;
background: rgba(255, 165, 0, 0.5);
}
`,
);

test(
'remove duplicate property with matching value, allow fallback',
removeExactDuplicates,
minify`
.a {
height: 10px;
}
.a {
height: 10px;
height: var(--linkHeight);
}
`,
minify`
.a {
height: 10px;
height: var(--linkHeight);
}
`,
);