Skip to content

Commit

Permalink
JIT: Optimize universal selector usage by inlining only the relevant …
Browse files Browse the repository at this point in the history
…selectors (#4850)

* WIP

* run prettier

* drop new lines in custom matcher

Drop all newlines, let prettier handle everything for us.

* add cache for the selector parser

* add `@apply` tests for the universal optimizer

* drop comments

* initial replacements

* WIP

* WIP

* MAKE IT WORK

* Rename to resolveDefaultsAtRules

* Update tests + defaults identifiers

* Don't add @defaults in AOT mode

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
  • Loading branch information
adamwathan and RobinMalfait committed Jul 1, 2021
1 parent 369c7b5 commit fe27356
Show file tree
Hide file tree
Showing 44 changed files with 1,172 additions and 488 deletions.
2 changes: 1 addition & 1 deletion jest/customMatchers.js
Expand Up @@ -102,7 +102,7 @@ expect.extend({
// This is probably naive but it's fast and works well enough.
toMatchFormattedCss(received, argument) {
function format(input) {
return prettier.format(input, {
return prettier.format(input.replace(/\n/g, ''), {
parser: 'css',
printWidth: 100,
})
Expand Down
69 changes: 69 additions & 0 deletions src/jit/lib/resolveDefaultsAtRules.js
@@ -0,0 +1,69 @@
import postcss from 'postcss'
import selectorParser from 'postcss-selector-parser'

let elementSelectorParser = selectorParser((selectors) => {
return selectors.map((s) => {
return s
.split((n) => n.type === 'combinator')
.pop()
.filter((n) => n.type !== 'pseudo' || n.value.startsWith('::'))
.join('')
.trim()
})
})

let cache = new Map()

function extractElementSelector(selector) {
if (!cache.has(selector)) {
cache.set(selector, elementSelectorParser.transformSync(selector))
}

return cache.get(selector)
}

export default function resolveDefaultsAtRules() {
return (root) => {
let variableNodeMap = new Map()
let universals = new Set()

root.walkAtRules('defaults', (rule) => {
if (rule.nodes.length > 0) {
universals.add(rule)
return
}

let variable = rule.params
if (!variableNodeMap.has(variable)) {
variableNodeMap.set(variable, new Set())
}

variableNodeMap.get(variable).add(rule.parent)

rule.remove()
})

for (let universal of universals) {
let selectors = new Set()

let rules = variableNodeMap.get(universal.params) ?? []

for (let rule of rules) {
for (let selector of extractElementSelector(rule.selector)) {
selectors.add(selector)
}
}

if (selectors.size === 0) {
universal.remove()
continue
}

let universalRule = postcss.rule()
universalRule.selectors = [...selectors]
universalRule.append(universal.nodes)
universal.before(universalRule)
universal.remove()
}
}
}
2 changes: 2 additions & 0 deletions src/jit/processTailwindFeatures.js
Expand Up @@ -3,6 +3,7 @@ import expandTailwindAtRules from './lib/expandTailwindAtRules'
import expandApplyAtRules from './lib/expandApplyAtRules'
import evaluateTailwindFunctions from '../lib/evaluateTailwindFunctions'
import substituteScreenAtRules from '../lib/substituteScreenAtRules'
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
import collapseAdjacentRules from './lib/collapseAdjacentRules'
import { createContext } from './lib/setupContextUtils'
import log from '../util/log'
Expand Down Expand Up @@ -45,6 +46,7 @@ export default function processTailwindFeatures(setupContext) {
expandApplyAtRules(context)(root, result)
evaluateTailwindFunctions(context)(root, result)
substituteScreenAtRules(context)(root, result)
resolveDefaultsAtRules(context)(root, result)
collapseAdjacentRules(context)(root, result)
}
}
7 changes: 6 additions & 1 deletion src/plugins/backdropBlur.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-blur': (value) => {
return {
'--tw-backdrop-blur': `blur(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropBrightness.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-brightness': (value) => {
return {
'--tw-backdrop-brightness': `brightness(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropContrast.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-contrast': (value) => {
return {
'--tw-backdrop-contrast': `contrast(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 5 additions & 2 deletions src/plugins/backdropFilter.js
Expand Up @@ -2,7 +2,7 @@ export default function () {
return function ({ config, addBase, addUtilities, variants }) {
if (config('mode') === 'jit') {
addBase({
'*, ::before, ::after': {
'@defaults backdrop-filter': {
'--tw-backdrop-blur': 'var(--tw-empty,/*!*/ /*!*/)',
'--tw-backdrop-brightness': 'var(--tw-empty,/*!*/ /*!*/)',
'--tw-backdrop-contrast': 'var(--tw-empty,/*!*/ /*!*/)',
Expand All @@ -27,7 +27,10 @@ export default function () {
})
addUtilities(
{
'.backdrop-filter': { 'backdrop-filter': 'var(--tw-backdrop-filter)' },
'.backdrop-filter': {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
},
'.backdrop-filter-none': { 'backdrop-filter': 'none' },
},
variants('backdropFilter')
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropGrayscale.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-grayscale': (value) => {
return {
'--tw-backdrop-grayscale': `grayscale(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropHueRotate.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-hue-rotate': (value) => {
return {
'--tw-backdrop-hue-rotate': `hue-rotate(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropInvert.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-invert': (value) => {
return {
'--tw-backdrop-invert': `invert(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropOpacity.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-opacity': (value) => {
return {
'--tw-backdrop-opacity': `opacity(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropSaturate.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-saturate': (value) => {
return {
'--tw-backdrop-saturate': `saturate(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/backdropSepia.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
'backdrop-sepia': (value) => {
return {
'--tw-backdrop-sepia': `sepia(${value})`,
...(config('mode') === 'jit' ? { 'backdrop-filter': 'var(--tw-backdrop-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults backdrop-filter': {},
'backdrop-filter': 'var(--tw-backdrop-filter)',
}
: {}),
}
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/blur.js
Expand Up @@ -5,7 +5,12 @@ export default function () {
blur: (value) => {
return {
'--tw-blur': `blur(${value})`,
...(config('mode') === 'jit' ? { filter: 'var(--tw-filter)' } : {}),
...(config('mode') === 'jit'
? {
'@defaults filter': {},
filter: 'var(--tw-filter)',
}
: {}),
}
},
},
Expand Down
44 changes: 31 additions & 13 deletions src/plugins/borderColor.js
Expand Up @@ -3,20 +3,38 @@ import withAlphaVariable from '../util/withAlphaVariable'

export default function () {
return function ({ config, addBase, matchUtilities, theme, variants, corePlugins }) {
if (!corePlugins('borderOpacity')) {
addBase({
'*, ::before, ::after': {
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
},
})
if (config('mode') === 'jit') {
if (!corePlugins('borderOpacity')) {
addBase({
'@defaults border-width': {
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
},
})
} else {
addBase({
'@defaults border-width': withAlphaVariable({
color: theme('borderColor.DEFAULT', 'currentColor'),
property: 'border-color',
variable: '--tw-border-opacity',
}),
})
}
} else {
addBase({
'*, ::before, ::after': withAlphaVariable({
color: theme('borderColor.DEFAULT', 'currentColor'),
property: 'border-color',
variable: '--tw-border-opacity',
}),
})
if (!corePlugins('borderOpacity')) {
addBase({
'*, ::before, ::after': {
'border-color': theme('borderColor.DEFAULT', 'currentColor'),
},
})
} else {
addBase({
'*, ::before, ::after': withAlphaVariable({
color: theme('borderColor.DEFAULT', 'currentColor'),
property: 'border-color',
variable: '--tw-border-opacity',
}),
})
}
}

matchUtilities(
Expand Down
44 changes: 31 additions & 13 deletions src/plugins/borderWidth.js
Expand Up @@ -2,17 +2,35 @@ import { asLength } from '../util/pluginUtils'
import createUtilityPlugin from '../util/createUtilityPlugin'

export default function () {
return createUtilityPlugin(
'borderWidth',
[
['border', ['border-width']],
[
['border-t', ['border-top-width']],
['border-r', ['border-right-width']],
['border-b', ['border-bottom-width']],
['border-l', ['border-left-width']],
],
],
{ resolveArbitraryValue: asLength }
)
return function (helpers) {
if (helpers.config('mode') === 'jit') {
createUtilityPlugin(
'borderWidth',
[
['border', [['@defaults border-width', {}], 'border-width']],
[
['border-t', [['@defaults border-width', {}], 'border-top-width']],
['border-r', [['@defaults border-width', {}], 'border-right-width']],
['border-b', [['@defaults border-width', {}], 'border-bottom-width']],
['border-l', [['@defaults border-width', {}], 'border-left-width']],
],
],
{ resolveArbitraryValue: asLength }
)(helpers)
} else {
createUtilityPlugin(
'borderWidth',
[
['border', ['border-width']],
[
['border-t', ['border-top-width']],
['border-r', ['border-right-width']],
['border-b', ['border-bottom-width']],
['border-l', ['border-left-width']],
],
],
{ resolveArbitraryValue: asLength }
)(helpers)
}
}
}

0 comments on commit fe27356

Please sign in to comment.