mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Improve experimental universal selector improvements (#5517)
* add dedicated tests for the experimenal universal selector improvements * Add failing test * keep pseudo elements * re-add logic for special types (class, id, attribute) Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This commit is contained in:
parent
f9ee118c01
commit
a8836eb093
@ -3,7 +3,20 @@ import selectorParser from 'postcss-selector-parser'
|
||||
import { flagEnabled } from '../featureFlags'
|
||||
|
||||
function minimumImpactSelector(nodes) {
|
||||
let pseudos = nodes.filter((n) => n.type === 'pseudo')
|
||||
let rest = nodes
|
||||
// Keep all pseudo & combinator types (:not([hidden]) ~ :not([hidden]))
|
||||
.filter((n) => n.type === 'pseudo' || n.type === 'combinator')
|
||||
// Remove leading pseudo's (:hover, :focus, ...)
|
||||
.filter((n, idx, all) => {
|
||||
// Keep pseudo elements
|
||||
if (n.type === 'pseudo' && n.value.startsWith('::')) return true
|
||||
|
||||
if (idx === 0 && n.type === 'pseudo') return false
|
||||
if (idx > 0 && n.type === 'pseudo' && all[idx - 1].type === 'pseudo') return false
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
let [bestNode] = nodes
|
||||
|
||||
for (let [type, getNode = (n) => n] of [
|
||||
@ -28,16 +41,12 @@ function minimumImpactSelector(nodes) {
|
||||
}
|
||||
}
|
||||
|
||||
return [bestNode, ...pseudos].join('').trim()
|
||||
return [bestNode, ...rest].join('').trim()
|
||||
}
|
||||
|
||||
let elementSelectorParser = selectorParser((selectors) => {
|
||||
return selectors.map((s) => {
|
||||
let nodes = s
|
||||
.split((n) => n.type === 'combinator')
|
||||
.pop()
|
||||
.filter((n) => n.type !== 'pseudo' || n.value.startsWith('::'))
|
||||
|
||||
let nodes = s.split((n) => n.type === 'combinator' && n.value === ' ').pop()
|
||||
return minimumImpactSelector(nodes)
|
||||
})
|
||||
})
|
||||
|
||||
209
tests/experimental.test.js
Normal file
209
tests/experimental.test.js
Normal file
@ -0,0 +1,209 @@
|
||||
import { run, html, css } from './util/run'
|
||||
|
||||
test('experimental universal selector improvements (box-shadow)', () => {
|
||||
let config = {
|
||||
experimental: 'all',
|
||||
content: [{ raw: html`<div class="shadow resize"></div>` }],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchCss(css`
|
||||
.shadow {
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
}
|
||||
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px 0 rgb(0 0 0 / 0.06);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
test('experimental universal selector improvements (pseudo hover)', () => {
|
||||
let config = {
|
||||
experimental: 'all',
|
||||
content: [{ raw: html`<div class="hover:shadow resize"></div>` }],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchCss(css`
|
||||
.hover\\:shadow {
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
}
|
||||
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.hover\\:shadow:hover {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px 0 rgb(0 0 0 / 0.06);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
test('experimental universal selector improvements (multiple classes: group)', () => {
|
||||
let config = {
|
||||
experimental: 'all',
|
||||
content: [{ raw: html`<div class="group-hover:shadow resize"></div>` }],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchCss(css`
|
||||
.group-hover\\:shadow {
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
}
|
||||
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.group:hover .group-hover\\:shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px 0 rgb(0 0 0 / 0.06);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
test('experimental universal selector improvements (child selectors: divide-y)', () => {
|
||||
let config = {
|
||||
experimental: 'all',
|
||||
content: [{ raw: html`<div class="divide-y resize"></div>` }],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchCss(css`
|
||||
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-divide-y-reverse: 0;
|
||||
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||
border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
test('experimental universal selector improvements (hover:divide-y)', () => {
|
||||
let config = {
|
||||
experimental: 'all',
|
||||
content: [{ raw: html`<div class="hover:divide-y resize"></div>` }],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchCss(css`
|
||||
.hover\\:divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.hover\\:divide-y:hover > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-divide-y-reverse: 0;
|
||||
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||
border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
test('experimental universal selector improvements (#app important)', () => {
|
||||
let config = {
|
||||
experimental: 'all',
|
||||
important: '#app',
|
||||
content: [{ raw: html`<div class="shadow divide-y resize"></div>` }],
|
||||
corePlugins: { preflight: false },
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchCss(css`
|
||||
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.shadow {
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
}
|
||||
|
||||
#app .resize {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
#app .divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-divide-y-reverse: 0;
|
||||
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||
border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
|
||||
}
|
||||
|
||||
#app .shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px 0 rgb(0 0 0 / 0.06);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||
var(--tw-shadow);
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
||||
@ -527,6 +527,7 @@ test('when a utility uses defaults but they do not exist', async () => {
|
||||
|
||||
test('selectors are reduced to the lowest possible specificity', async () => {
|
||||
let config = {
|
||||
experimental: 'all',
|
||||
content: [{ raw: html`<div class="foo"></div>` }],
|
||||
corePlugins: [],
|
||||
}
|
||||
@ -576,9 +577,13 @@ test('selectors are reduced to the lowest possible specificity', async () => {
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.css).toMatchFormattedCss(css`
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
.foo,
|
||||
[id='app'],
|
||||
[id='page'],
|
||||
[id='other'],
|
||||
[data-bar='baz'],
|
||||
article,
|
||||
[id='another']::before {
|
||||
--color: black;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user