mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Dedupe duplicate properties (#5830)
* add `collapseDuplicateDeclarations`
This will allow us to remove duplicate declarations. This occurs when
you are using `@apply` for example.
The reason I implemented it as a separate step, is because this doesn't
only happen for `@apply`, but it also happens if you do something like:
```js
addComponents({ '.btn-blue, .btm-red': { padding: '10px' } })
```
So instead of tracking down every place this is happening, it now
happens at the very end.
* use new plugin in processTailwindFeatures
* add/update tests by removing duplicate declarations
* update changelog
This commit is contained in:
parent
0c2f1a6472
commit
ea6f14a499
@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Configure chokidar's `awaitWriteFinish` setting to avoid occasional stale builds on Windows ([#5774](https://github.com/tailwindlabs/tailwindcss/pull/5774))
|
||||
- Fix CLI `--content` option ([#5775](https://github.com/tailwindlabs/tailwindcss/pull/5775))
|
||||
- Fix before/after utilities overriding custom content values at larger breakpoints ([#5820](https://github.com/tailwindlabs/tailwindcss/pull/5820))
|
||||
- Cleanup duplicate properties ([#5830](https://github.com/tailwindlabs/tailwindcss/pull/5830))
|
||||
|
||||
## [3.0.0-alpha.1] - 2021-10-01
|
||||
|
||||
|
||||
28
src/lib/collapseDuplicateDeclarations.js
Normal file
28
src/lib/collapseDuplicateDeclarations.js
Normal file
@ -0,0 +1,28 @@
|
||||
export default function collapseDuplicateDeclarations() {
|
||||
return (root) => {
|
||||
root.walkRules((node) => {
|
||||
let seen = new Map()
|
||||
let droppable = new Set([])
|
||||
|
||||
node.walkDecls((decl) => {
|
||||
// This could happen if we have nested selectors. In that case the
|
||||
// parent will loop over all its declarations but also the declarations
|
||||
// of nested rules. With this we ensure that we are shallowly checking
|
||||
// declarations.
|
||||
if (decl.parent !== node) {
|
||||
return
|
||||
}
|
||||
|
||||
if (seen.has(decl.prop)) {
|
||||
droppable.add(seen.get(decl.prop))
|
||||
}
|
||||
|
||||
seen.set(decl.prop, decl)
|
||||
})
|
||||
|
||||
for (let decl of droppable) {
|
||||
decl.remove()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import evaluateTailwindFunctions from './lib/evaluateTailwindFunctions'
|
||||
import substituteScreenAtRules from './lib/substituteScreenAtRules'
|
||||
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
|
||||
import collapseAdjacentRules from './lib/collapseAdjacentRules'
|
||||
import collapseDuplicateDeclarations from './lib/collapseDuplicateDeclarations'
|
||||
import detectNesting from './lib/detectNesting'
|
||||
import { createContext } from './lib/setupContextUtils'
|
||||
import { issueFlagNotices } from './featureFlags'
|
||||
@ -42,5 +43,6 @@ export default function processTailwindFeatures(setupContext) {
|
||||
substituteScreenAtRules(context)(root, result)
|
||||
resolveDefaultsAtRules(context)(root, result)
|
||||
collapseAdjacentRules(context)(root, result)
|
||||
collapseDuplicateDeclarations(context)(root, result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,8 +10,6 @@
|
||||
.class-order {
|
||||
padding: 2rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
padding-top: 1.75rem;
|
||||
padding-bottom: 1.75rem;
|
||||
padding-top: 1rem;
|
||||
padding-right: 0.25rem;
|
||||
@ -127,7 +125,6 @@
|
||||
/* TODO: This works but the generated CSS is unnecessarily verbose. */
|
||||
.complex-utilities {
|
||||
--tw-ordinal: ordinal;
|
||||
font-variant-numeric: var(--tw-font-variant-numeric);
|
||||
--tw-numeric-spacing: tabular-nums;
|
||||
font-variant-numeric: var(--tw-font-variant-numeric);
|
||||
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.05);
|
||||
@ -155,7 +152,6 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
.use-dependant-only-b {
|
||||
font-weight: 700;
|
||||
font-weight: 400;
|
||||
}
|
||||
.btn {
|
||||
|
||||
@ -351,7 +351,6 @@ test('@applying classes from outside a @layer respects the source order', async
|
||||
await run(input, config).then((result) => {
|
||||
return expect(result.css).toMatchFormattedCss(css`
|
||||
.baz {
|
||||
text-decoration: underline;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@ -402,3 +401,30 @@ test('@applying classes from outside a @layer respects the source order', async
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
it('should remove duplicate properties when using apply with similar properties', () => {
|
||||
let config = {
|
||||
content: [{ raw: 'foo' }],
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind utilities;
|
||||
|
||||
.foo {
|
||||
@apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2;
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
.foo {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
--tw-translate-x: -50%;
|
||||
--tw-translate-y: -50%;
|
||||
transform: var(--tw-transform);
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
@ -868,7 +868,6 @@ test('when important is a selector it scopes all selectors in a rule, even thoug
|
||||
#app .custom-rotate-90,
|
||||
#app .custom-rotate-1\/4 {
|
||||
transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
`)
|
||||
})
|
||||
@ -952,7 +951,6 @@ test('all selectors in a rule are prefixed', () => {
|
||||
.tw-btn-blue,
|
||||
.tw-btn-red {
|
||||
padding: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
@ -199,7 +199,6 @@ div {
|
||||
}
|
||||
.test-apply-font-variant {
|
||||
--tw-ordinal: ordinal;
|
||||
font-variant-numeric: var(--tw-font-variant-numeric);
|
||||
--tw-numeric-spacing: tabular-nums;
|
||||
font-variant-numeric: var(--tw-font-variant-numeric);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user