Skip to content

Commit 615c157

Browse files
Don't prefix arbitrary classes in peer/group variants (#11454)
* 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 <[email protected]>
1 parent 6722e78 commit 615c157

8 files changed

+129
-60
lines changed

src/corePlugins.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadow
2121
import { removeAlphaVariables } from './util/removeAlphaVariables'
2222
import { flagEnabled } from './featureFlags'
2323
import { normalize } from './util/dataTypes'
24+
import { INTERNAL_FEATURES } from './lib/setupContextUtils'
2425

2526
export let variantPlugins = {
2627
pseudoElementVariants: ({ addVariant }) => {
@@ -79,7 +80,7 @@ export let variantPlugins = {
7980
})
8081
},
8182

82-
pseudoClassVariants: ({ addVariant, matchVariant, config }) => {
83+
pseudoClassVariants: ({ addVariant, matchVariant, config, prefix }) => {
8384
let pseudoVariants = [
8485
// Positional
8586
['first', '&:first-child'],
@@ -150,12 +151,12 @@ export let variantPlugins = {
150151
let variants = {
151152
group: (_, { modifier }) =>
152153
modifier
153-
? [`:merge(.group\\/${escapeClassName(modifier)})`, ' &']
154-
: [`:merge(.group)`, ' &'],
154+
? [`:merge(${prefix('.group')}\\/${escapeClassName(modifier)})`, ' &']
155+
: [`:merge(${prefix('.group')})`, ' &'],
155156
peer: (_, { modifier }) =>
156157
modifier
157-
? [`:merge(.peer\\/${escapeClassName(modifier)})`, ' ~ &']
158-
: [`:merge(.peer)`, ' ~ &'],
158+
? [`:merge(${prefix('.peer')}\\/${escapeClassName(modifier)})`, ' ~ &']
159+
: [`:merge(${prefix('.peer')})`, ' ~ &'],
159160
}
160161

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

192193
return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end)
193194
},
194-
{ values: Object.fromEntries(pseudoVariants) }
195+
{
196+
values: Object.fromEntries(pseudoVariants),
197+
[INTERNAL_FEATURES]: {
198+
respectPrefix: false,
199+
},
200+
}
195201
)
196202
}
197203
},

src/lib/generateRules.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '../util/formatVariantSelector'
1414
import { asClass } from '../util/nameClass'
1515
import { normalize } from '../util/dataTypes'
16-
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
16+
import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils'
1717
import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue'
1818
import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js'
1919
import { flagEnabled } from '../featureFlags'
@@ -226,9 +226,16 @@ function applyVariant(variant, matches, context) {
226226

227227
if (context.variantMap.has(variant)) {
228228
let isArbitraryVariant = isArbitraryValue(variant)
229+
let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {}
229230
let variantFunctionTuples = context.variantMap.get(variant).slice()
230231
let result = []
231232

233+
let respectPrefix = (() => {
234+
if (isArbitraryVariant) return false
235+
if (internalFeatures.respectPrefix === false) return false
236+
return true
237+
})()
238+
232239
for (let [meta, rule] of matches) {
233240
// Don't generate variants for user css
234241
if (meta.layer === 'user') {
@@ -289,7 +296,7 @@ function applyVariant(variant, matches, context) {
289296
format(selectorFormat) {
290297
collectedFormats.push({
291298
format: selectorFormat,
292-
isArbitraryVariant,
299+
respectPrefix,
293300
})
294301
},
295302
args,
@@ -318,7 +325,7 @@ function applyVariant(variant, matches, context) {
318325
if (typeof ruleWithVariant === 'string') {
319326
collectedFormats.push({
320327
format: ruleWithVariant,
321-
isArbitraryVariant,
328+
respectPrefix,
322329
})
323330
}
324331

@@ -362,7 +369,7 @@ function applyVariant(variant, matches, context) {
362369
// format: .foo &
363370
collectedFormats.push({
364371
format: modified.replace(rebuiltBase, '&'),
365-
isArbitraryVariant,
372+
respectPrefix,
366373
})
367374
rule.selector = before
368375
})

src/lib/setupContextUtils.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { hasContentChanged } from './cacheInvalidation.js'
2323
import { Offsets } from './offsets.js'
2424
import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector'
2525

26+
export const INTERNAL_FEATURES = Symbol()
27+
2628
const VARIANT_TYPES = {
2729
AddVariant: Symbol.for('ADD_VARIANT'),
2830
MatchVariant: Symbol.for('MATCH_VARIANT'),
@@ -1110,17 +1112,24 @@ function registerPlugins(plugins, context) {
11101112
}
11111113

11121114
let isArbitraryVariant = !(value in (options.values ?? {}))
1115+
let internalFeatures = options[INTERNAL_FEATURES] ?? {}
1116+
1117+
let respectPrefix = (() => {
1118+
if (isArbitraryVariant) return false
1119+
if (internalFeatures.respectPrefix === false) return false
1120+
return true
1121+
})()
11131122

11141123
formatStrings = formatStrings.map((format) =>
11151124
format.map((str) => ({
11161125
format: str,
1117-
isArbitraryVariant,
1126+
respectPrefix,
11181127
}))
11191128
)
11201129

11211130
manualFormatStrings = manualFormatStrings.map((format) => ({
11221131
format,
1123-
isArbitraryVariant,
1132+
respectPrefix,
11241133
}))
11251134

11261135
let opts = {

src/util/formatVariantSelector.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { movePseudos } from './pseudoElements'
99
/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
1010
/** @typedef {import('postcss-selector-parser').Node} Node */
1111

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

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

3030
return {
3131
...format,
32-
ast: format.isArbitraryVariant ? ast : prefixSelector(prefix, ast),
32+
ast: format.respectPrefix ? prefixSelector(prefix, ast) : ast,
3333
}
3434
})
3535

src/util/prefixSelector.js

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default function (prefix, selector, prependNegative = false) {
1717
return selector
1818
}
1919

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

2223
ast.walkClasses((classSelector) => {

0 commit comments

Comments
 (0)