Skip to content

Commit

Permalink
Don't prefix arbitrary classes in peer/group variants (#11454)
Browse files Browse the repository at this point in the history
* Refactor

* Don’t prefix classes in arbitrary values for group and peer

* use `foo` instead of `lol`

* handle the prefix inside the group/peer variants

Then add the `NoPrefix` feature to the variant itself, which will skip
prefixing any other class in the generated selector (because we already
took care of prefixing `.group` and `.peer`).

We are using an internal symbol such that:

- We can keep it as a private API
- We don't introduce a breaking change

* refactor to simple object instead

We will still use a symbol as an internal/private marker, but the data
itself will be a simple object for now.

If we want to refactor this (and more) in the future using bitflags then
we can refactor that in a separate PR.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
  • Loading branch information
thecrypticace and RobinMalfait committed Jun 28, 2023
1 parent 6722e78 commit 615c157
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 60 deletions.
18 changes: 12 additions & 6 deletions src/corePlugins.js
Expand Up @@ -21,6 +21,7 @@ import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadow
import { removeAlphaVariables } from './util/removeAlphaVariables'
import { flagEnabled } from './featureFlags'
import { normalize } from './util/dataTypes'
import { INTERNAL_FEATURES } from './lib/setupContextUtils'

export let variantPlugins = {
pseudoElementVariants: ({ addVariant }) => {
Expand Down Expand Up @@ -79,7 +80,7 @@ export let variantPlugins = {
})
},

pseudoClassVariants: ({ addVariant, matchVariant, config }) => {
pseudoClassVariants: ({ addVariant, matchVariant, config, prefix }) => {
let pseudoVariants = [
// Positional
['first', '&:first-child'],
Expand Down Expand Up @@ -150,12 +151,12 @@ export let variantPlugins = {
let variants = {
group: (_, { modifier }) =>
modifier
? [`:merge(.group\\/${escapeClassName(modifier)})`, ' &']
: [`:merge(.group)`, ' &'],
? [`:merge(${prefix('.group')}\\/${escapeClassName(modifier)})`, ' &']
: [`:merge(${prefix('.group')})`, ' &'],
peer: (_, { modifier }) =>
modifier
? [`:merge(.peer\\/${escapeClassName(modifier)})`, ' ~ &']
: [`:merge(.peer)`, ' ~ &'],
? [`:merge(${prefix('.peer')}\\/${escapeClassName(modifier)})`, ' ~ &']
: [`:merge(${prefix('.peer')})`, ' ~ &'],
}

for (let [name, fn] of Object.entries(variants)) {
Expand Down Expand Up @@ -191,7 +192,12 @@ export let variantPlugins = {

return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end)
},
{ values: Object.fromEntries(pseudoVariants) }
{
values: Object.fromEntries(pseudoVariants),
[INTERNAL_FEATURES]: {
respectPrefix: false,
},
}
)
}
},
Expand Down
15 changes: 11 additions & 4 deletions src/lib/generateRules.js
Expand Up @@ -13,7 +13,7 @@ import {
} from '../util/formatVariantSelector'
import { asClass } from '../util/nameClass'
import { normalize } from '../util/dataTypes'
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
import { flagEnabled } from '../featureFlags'
Expand Down Expand Up @@ -226,9 +226,16 @@ function applyVariant(variant, matches, context) {

if (context.variantMap.has(variant)) {
let isArbitraryVariant = isArbitraryValue(variant)
let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
let variantFunctionTuples = context.variantMap.get(variant).slice()
let result = []

let respectPrefix = (() => {
if (isArbitraryVariant) return false
if (internalFeatures.respectPrefix === false) return false
return true
})()

for (let [meta, rule] of matches) {
// Don't generate variants for user css
if (meta.layer === 'user') {
Expand Down Expand Up @@ -289,7 +296,7 @@ function applyVariant(variant, matches, context) {
format(selectorFormat) {
collectedFormats.push({
format: selectorFormat,
isArbitraryVariant,
respectPrefix,
})
},
args,
Expand Down Expand Up @@ -318,7 +325,7 @@ function applyVariant(variant, matches, context) {
if (typeof ruleWithVariant === 'string') {
collectedFormats.push({
format: ruleWithVariant,
isArbitraryVariant,
respectPrefix,
})
}

Expand Down Expand Up @@ -362,7 +369,7 @@ function applyVariant(variant, matches, context) {
// format: .foo &
collectedFormats.push({
format: modified.replace(rebuiltBase, '&'),
isArbitraryVariant,
respectPrefix,
})
rule.selector = before
})
Expand Down
13 changes: 11 additions & 2 deletions src/lib/setupContextUtils.js
Expand Up @@ -23,6 +23,8 @@ import { hasContentChanged } from './cacheInvalidation.js'
import { Offsets } from './offsets.js'
import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'

export const INTERNAL_FEATURES = Symbol()

const VARIANT_TYPES = {
AddVariant: Symbol.for('ADD_VARIANT'),
MatchVariant: Symbol.for('MATCH_VARIANT'),
Expand Down Expand Up @@ -1110,17 +1112,24 @@ function registerPlugins(plugins, context) {
}

let isArbitraryVariant = !(value in (options.values ?? {}))
let internalFeatures = options[INTERNAL_FEATURES] ?? {}

let respectPrefix = (() => {
if (isArbitraryVariant) return false
if (internalFeatures.respectPrefix === false) return false
return true
})()

formatStrings = formatStrings.map((format) =>
format.map((str) => ({
format: str,
isArbitraryVariant,
respectPrefix,
}))
)

manualFormatStrings = manualFormatStrings.map((format) => ({
format,
isArbitraryVariant,
respectPrefix,
}))

let opts = {
Expand Down
4 changes: 2 additions & 2 deletions src/util/formatVariantSelector.js
Expand Up @@ -9,7 +9,7 @@ import { movePseudos } from './pseudoElements'
/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
/** @typedef {import('postcss-selector-parser').Node} Node */

/** @typedef {{format: string, isArbitraryVariant: boolean}[]} RawFormats */
/** @typedef {{format: string, respectPrefix: boolean}[]} RawFormats */
/** @typedef {import('postcss-selector-parser').Root} ParsedFormats */
/** @typedef {RawFormats | ParsedFormats} AcceptedFormats */

Expand All @@ -29,7 +29,7 @@ export function formatVariantSelector(formats, { context, candidate }) {

return {
...format,
ast: format.isArbitraryVariant ? ast : prefixSelector(prefix, ast),
ast: format.respectPrefix ? prefixSelector(prefix, ast) : ast,
}
})

Expand Down
1 change: 1 addition & 0 deletions src/util/prefixSelector.js
Expand Up @@ -17,6 +17,7 @@ export default function (prefix, selector, prependNegative = false) {
return selector
}

/** @type {import('postcss-selector-parser').Root} */
let ast = typeof selector === 'string' ? parser().astSync(selector) : selector

ast.walkClasses((classSelector) => {
Expand Down

0 comments on commit 615c157

Please sign in to comment.