Try to use lowest impact selector when filling in defaults (#4866)

This commit is contained in:
Adam Wathan 2021-07-01 17:18:46 -04:00 committed by GitHub
parent 81e9f651e4
commit 4a070ac0be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 134 additions and 4 deletions

View File

@ -1,14 +1,43 @@
import postcss from 'postcss'
import selectorParser from 'postcss-selector-parser'
function minimumImpactSelector(nodes) {
let pseudos = nodes.filter((n) => n.type === 'pseudo')
let [bestNode] = nodes
for (let [type, getNode = (n) => n] of [
['class'],
[
'id',
(n) =>
selectorParser.attribute({
attribute: 'id',
operator: '=',
value: n.value,
quoteMark: '"',
}),
],
['attribute'],
]) {
let match = nodes.find((n) => n.type === type)
if (match) {
bestNode = getNode(match)
break
}
}
return [bestNode, ...pseudos].join('').trim()
}
let elementSelectorParser = selectorParser((selectors) => {
return selectors.map((s) => {
return s
let nodes = s
.split((n) => n.type === 'combinator')
.pop()
.filter((n) => n.type !== 'pseudo' || n.value.startsWith('::'))
.join('')
.trim()
return minimumImpactSelector(nodes)
})
})
@ -28,7 +57,7 @@ export default function resolveDefaultsAtRules() {
let universals = new Set()
root.walkAtRules('defaults', (rule) => {
if (rule.nodes.length > 0) {
if (rule.nodes && rule.nodes.length > 0) {
universals.add(rule)
return
}

View File

@ -605,3 +605,104 @@ test('when a utility uses defaults but they do not exist', async () => {
`)
})
})
test('selectors are reduced to the lowest possible specificity', async () => {
let config = {
mode: 'jit',
purge: [
{
raw: '<div class="foo"></div>',
},
],
theme: {},
plugins: [],
corePlugins: [],
}
let css = `
@defaults test {
--color: black;
}
/* --- */
.foo {
@defaults test;
background-color: var(--color);
}
#app {
@defaults test;
border-color: var(--color);
}
span#page {
@defaults test;
color: var(--color);
}
div[data-foo="bar"]#other {
@defaults test;
fill: var(--color);
}
div[data-bar="baz"] {
@defaults test;
stroke: var(--color);
}
article {
@defaults test;
--article: var(--color);
}
div[data-foo="bar"]#another::before {
@defaults test;
fill: var(--color);
}
`
return run(css, config).then((result) => {
expect(result.css).toMatchFormattedCss(`
.foo,
[id="app"],
[id="page"],
[id="other"],
[data-bar="baz"],
article,
[id="another"]::before {
--color: black;
}
/* --- */
.foo {
background-color: var(--color);
}
#app {
border-color: var(--color);
}
span#page {
color: var(--color);
}
div[data-foo="bar"]#other {
fill: var(--color);
}
div[data-bar="baz"] {
stroke: var(--color);
}
article {
--article: var(--color);
}
div[data-foo="bar"]#another::before {
fill: var(--color);
}
`)
})
})