JIT: Add support for before/after pseudo-elements (#4461)

This commit is contained in:
Adam Wathan 2021-05-26 08:57:02 -04:00 • committed by GitHub
parent 1badba55b7
commit c8a5f816f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 130 additions and 23 deletions

View file

@ -1,8 +1,11 @@
import * as plugins from './plugins/index.js'
import configurePlugins from './util/configurePlugins'
const jitOnlyPlugins = ['content']
export default function ({ corePlugins: corePluginConfig }) {
corePluginConfig = corePluginConfig.filter((pluginName) => !jitOnlyPlugins.includes(pluginName))
return configurePlugins(corePluginConfig, Object.keys(plugins)).map((pluginName) => {
return plugins[pluginName]()
})

View file

@ -12,6 +12,50 @@ import {
export default {
pseudoClassVariants: function ({ config, addVariant }) {
addVariant(
'before',
transformAllSelectors(
(selector) => {
return updateAllClasses(selector, (className, { withPseudo }) => {
return withPseudo(`before${config('separator')}${className}`, '::before')
})
},
{
withRule: (rule) => {
let foundContent = false
rule.walkDecls('content', () => {
foundContent = true
})
if (!foundContent) {
rule.prepend(postcss.decl({ prop: 'content', value: '""' }))
}
},
}
)
)
addVariant(
'after',
transformAllSelectors(
(selector) => {
return updateAllClasses(selector, (className, { withPseudo }) => {
return withPseudo(`after${config('separator')}${className}`, '::after')
})
},
{
withRule: (rule) => {
let foundContent = false
rule.walkDecls('content', () => {
foundContent = true
})
if (!foundContent) {
rule.prepend(postcss.decl({ prop: 'content', value: '""' }))
}
},
}
)
)
let pseudoVariants = [
['first', 'first-child'],
['last', 'last-child'],
@ -35,7 +79,7 @@ export default {
addVariant(
variantName,
transformAllClasses((className, { withPseudo }) => {
return withPseudo(`${variantName}${config('separator')}${className}`, state)
return withPseudo(`${variantName}${config('separator')}${className}`, `:${state}`)
})
)
}
@ -95,11 +139,13 @@ export default {
(className) => {
return `motion-safe${config('separator')}${className}`
},
() =>
postcss.atRule({
name: 'media',
params: '(prefers-reduced-motion: no-preference)',
})
{
wrap: () =>
postcss.atRule({
name: 'media',
params: '(prefers-reduced-motion: no-preference)',
}),
}
)
)
@ -109,11 +155,13 @@ export default {
(className) => {
return `motion-reduce${config('separator')}${className}`
},
() =>
postcss.atRule({
name: 'media',
params: '(prefers-reduced-motion: reduce)',
})
{
wrap: () =>
postcss.atRule({
name: 'media',
params: '(prefers-reduced-motion: reduce)',
}),
}
)
)
},
@ -142,11 +190,13 @@ export default {
(className) => {
return `dark${config('separator')}${className}`
},
() =>
postcss.atRule({
name: 'media',
params: '(prefers-color-scheme: dark)',
})
{
wrap: () =>
postcss.atRule({
name: 'media',
params: '(prefers-color-scheme: dark)',
}),
}
)
)
}
@ -162,7 +212,7 @@ export default {
(className) => {
return `${screen}${config('separator')}${className}`
},
() => postcss.atRule({ name: 'media', params: query })
{ wrap: () => postcss.atRule({ name: 'media', params: query }) }
)
)
}

5
src/plugins/content.js Normal file
View file

@ -0,0 +1,5 @@
import createUtilityPlugin from '../util/createUtilityPlugin'
export default function () {
return createUtilityPlugin('content')
}

View file

@ -161,3 +161,5 @@ export { default as transitionProperty } from './transitionProperty'
export { default as transitionDelay } from './transitionDelay'
export { default as transitionDuration } from './transitionDuration'
export { default as transitionTimingFunction } from './transitionTimingFunction'
export { default as content } from './content'

View file

@ -9,7 +9,7 @@ export function updateAllClasses(selectors, updateClass) {
selectors.walkClasses((sel) => {
let updatedClass = updateClass(sel.value, {
withPseudo(className, pseudo) {
sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `:${pseudo}` }))
sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `${pseudo}` }))
return className
},
})
@ -36,7 +36,7 @@ export function updateLastClasses(selectors, updateClass) {
let updatedClass = updateClass(lastClass.value, {
withPseudo(className, pseudo) {
lastClass.parent.insertAfter(lastClass, selectorParser.pseudo({ value: `:${pseudo}` }))
lastClass.parent.insertAfter(lastClass, selectorParser.pseudo({ value: `${pseudo}` }))
return className
},
})
@ -51,11 +51,14 @@ export function updateLastClasses(selectors, updateClass) {
return result
}
export function transformAllSelectors(transformSelector, wrap = null) {
export function transformAllSelectors(transformSelector, { wrap, withRule } = {}) {
return ({ container }) => {
container.walkRules((rule) => {
let transformed = rule.selector.split(',').map(transformSelector).join(',')
rule.selector = transformed
if (withRule) {
withRule(rule)
}
return rule
})
@ -67,23 +70,35 @@ export function transformAllSelectors(transformSelector, wrap = null) {
}
}
export function transformAllClasses(transformClass) {
export function transformAllClasses(transformClass, { wrap, withRule } = {}) {
return ({ container }) => {
container.walkRules((rule) => {
let selector = rule.selector
let variantSelector = updateAllClasses(selector, transformClass)
rule.selector = variantSelector
if (withRule) {
withRule(rule)
}
return rule
})
if (wrap) {
let wrapper = wrap()
wrapper.append(container.nodes)
container.append(wrapper)
}
}
}
export function transformLastClasses(transformClass, wrap = null) {
export function transformLastClasses(transformClass, { wrap, withRule } = {}) {
return ({ container }) => {
container.walkRules((rule) => {
let selector = rule.selector
let variantSelector = updateLastClasses(selector, transformClass)
rule.selector = variantSelector
if (withRule) {
withRule(rule)
}
return rule
})