Skip to content

Commit d6bec49

Browse files
Fix parallel variant ordering clash (#9282)
* Remove remnants of the user layer It hasn’t been used in a while * Rewrite sort offset generation * wip * wip wip * Handle parasite utilities * wip * wip * Make parallel variants sorting more resillient It’s not perfect but it’s close * fix * remove todo it adds a new bit so it can’t * Simplify getClassOrder usage * Simplify oops oops * Add parasite utility for `dark` dark mode class name * Cleanup * Cleanup * Simplify * format files * Fix prettier plugin to use git build of Tailwind CSS Symlink and build instead of adding a recursive dev dependency It breaks node < 16 * Fix prettier error * wip * fix test * Update changelog Co-authored-by: Robin Malfait <[email protected]>
1 parent 88e98f5 commit d6bec49

10 files changed

+321
-129
lines changed

.github/workflows/nodejs.yml

+7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ jobs:
4141
env:
4242
CI: true
4343

44+
- name: Link and build Tailwind CSS
45+
run: |
46+
npm run swcify
47+
ln -nfs `pwd` node_modules/tailwindcss
48+
env:
49+
CI: true
50+
4451
- name: Test
4552
run: npm test
4653
env:

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828
- Fix `fontFamily` config TypeScript types ([#9214](https://github.com/tailwindlabs/tailwindcss/pull/9214))
2929
- Handle variants on complex selector utilities ([#9262](https://github.com/tailwindlabs/tailwindcss/pull/9262))
3030
- Don't mutate shared config objects ([#9294](https://github.com/tailwindlabs/tailwindcss/pull/9294))
31+
- Fix ordering of parallel variants ([#9282](https://github.com/tailwindlabs/tailwindcss/pull/9282))
3132

3233
## [3.1.8] - 2022-08-05
3334

src/lib/expandApplyAtRules.js

+5-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import postcss from 'postcss'
22
import parser from 'postcss-selector-parser'
33

44
import { resolveMatches } from './generateRules'
5-
import bigSign from '../util/bigSign'
65
import escapeClassName from '../util/escapeClassName'
76

87
/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */
@@ -149,9 +148,7 @@ function buildLocalApplyCache(root, context) {
149148
/** @type {ApplyCache} */
150149
let cache = new Map()
151150

152-
let highestOffset = context.layerOrder.user >> 4n
153-
154-
root.walkRules((rule, idx) => {
151+
root.walkRules((rule) => {
155152
// Ignore rules generated by Tailwind
156153
for (let node of pathToRoot(rule)) {
157154
if (node.raws.tailwind?.layer !== undefined) {
@@ -161,6 +158,7 @@ function buildLocalApplyCache(root, context) {
161158

162159
// Clone what's required to represent this singular rule in the tree
163160
let container = nestedClone(rule)
161+
let sort = context.offsets.create('user')
164162

165163
for (let className of extractClasses(rule)) {
166164
let list = cache.get(className) || []
@@ -169,7 +167,7 @@ function buildLocalApplyCache(root, context) {
169167
list.push([
170168
{
171169
layer: 'user',
172-
sort: BigInt(idx) + highestOffset,
170+
sort,
173171
important: false,
174172
},
175173
container,
@@ -553,16 +551,12 @@ function processApply(root, context, localCache) {
553551
}
554552

555553
// Insert it
556-
siblings.push([
557-
// Ensure that when we are sorting, that we take the layer order into account
558-
{ ...meta, sort: meta.sort | context.layerOrder[meta.layer] },
559-
root.nodes[0],
560-
])
554+
siblings.push([meta.sort, root.nodes[0]])
561555
}
562556
}
563557

564558
// Inject the rules, sorted, correctly
565-
let nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map((s) => s[1])
559+
let nodes = context.offsets.sort(siblings).map((s) => s[1])
566560

567561
// `parent` refers to the node at `.abc` in: .abc { @apply mt-2 }
568562
parent.after(nodes)

src/lib/expandTailwindAtRules.js

+7-41
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import LRU from 'quick-lru'
22
import * as sharedState from './sharedState'
33
import { generateRules } from './generateRules'
4-
import bigSign from '../util/bigSign'
54
import log from '../util/log'
65
import cloneNodes from '../util/cloneNodes'
76
import { defaultExtractor } from './defaultExtractor'
@@ -74,57 +73,24 @@ function getClassCandidates(content, extractor, candidates, seen) {
7473
}
7574
}
7675

76+
/**
77+
*
78+
* @param {[import('./offsets.js').RuleOffset, import('postcss').Node][]} rules
79+
* @param {*} context
80+
*/
7781
function buildStylesheet(rules, context) {
78-
let sortedRules = rules.sort(([a], [z]) => bigSign(a - z))
82+
let sortedRules = context.offsets.sort(rules)
7983

8084
let returnValue = {
8185
base: new Set(),
8286
defaults: new Set(),
8387
components: new Set(),
8488
utilities: new Set(),
8589
variants: new Set(),
86-
87-
// All the CSS that is not Tailwind related can be put in this bucket. This
88-
// will make it easier to later use this information when we want to
89-
// `@apply` for example. The main reason we do this here is because we
90-
// still need to make sure the order is correct. Last but not least, we
91-
// will make sure to always re-inject this section into the css, even if
92-
// certain rules were not used. This means that it will look like a no-op
93-
// from the user's perspective, but we gathered all the useful information
94-
// we need.
95-
user: new Set(),
9690
}
9791

9892
for (let [sort, rule] of sortedRules) {
99-
if (sort >= context.minimumScreen) {
100-
returnValue.variants.add(rule)
101-
continue
102-
}
103-
104-
if (sort & context.layerOrder.base) {
105-
returnValue.base.add(rule)
106-
continue
107-
}
108-
109-
if (sort & context.layerOrder.defaults) {
110-
returnValue.defaults.add(rule)
111-
continue
112-
}
113-
114-
if (sort & context.layerOrder.components) {
115-
returnValue.components.add(rule)
116-
continue
117-
}
118-
119-
if (sort & context.layerOrder.utilities) {
120-
returnValue.utilities.add(rule)
121-
continue
122-
}
123-
124-
if (sort & context.layerOrder.user) {
125-
returnValue.user.add(rule)
126-
continue
127-
}
93+
returnValue[sort.layer].add(rule)
12894
}
12995

13096
return returnValue

src/lib/generateRules.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ function applyVariant(variant, matches, context) {
146146

147147
let fn = parseVariant(selector)
148148

149-
let sort = Array.from(context.variantOrder.values()).pop() << 1n
149+
let sort = context.offsets.recordVariant(variant)
150+
150151
context.variantMap.set(variant, [[sort, fn]])
151-
context.variantOrder.set(variant, sort)
152152
}
153153

154154
if (context.variantMap.has(variant)) {
@@ -227,11 +227,7 @@ function applyVariant(variant, matches, context) {
227227
// where you keep handling jobs until everything is done and each job can queue more
228228
// jobs if needed.
229229
variantFunctionTuples.push([
230-
// TODO: This could have potential bugs if we shift the sort order from variant A far
231-
// enough into the sort space of variant B. The chances are low, but if this happens
232-
// then this might be the place too look at. One potential solution to this problem is
233-
// reserving additional X places for these 'unknown' variants in between.
234-
variantSort | BigInt(idx << ruleWithVariant.length),
230+
context.offsets.applyParallelOffset(variantSort, idx),
235231
variantFunction,
236232

237233
// If the clone has been modified we have to pass that back
@@ -298,7 +294,7 @@ function applyVariant(variant, matches, context) {
298294
let withOffset = [
299295
{
300296
...meta,
301-
sort: variantSort | meta.sort,
297+
sort: context.offsets.applyVariantOffset(meta.sort, variantSort),
302298
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
303299
isArbitraryVariant: isArbitraryValue(variant),
304300
},
@@ -409,9 +405,11 @@ function extractArbitraryProperty(classCandidate, context) {
409405
return null
410406
}
411407

408+
let sort = context.offsets.arbitraryProperty()
409+
412410
return [
413411
[
414-
{ sort: context.arbitraryPropertiesSort, layer: 'utilities' },
412+
{ sort, layer: 'utilities' },
415413
() => ({
416414
[asClass(classCandidate)]: {
417415
[property]: normalized,
@@ -704,15 +702,15 @@ function generateRules(candidates, context) {
704702
context.candidateRuleCache.set(candidate, rules)
705703

706704
for (const match of matches) {
707-
let [{ sort, layer, options }, rule] = match
705+
let [{ sort, options }, rule] = match
708706

709707
if (options.respectImportant && strategy) {
710708
let container = postcss.root({ nodes: [rule.clone()] })
711709
container.walkRules(strategy)
712710
rule = container.nodes[0]
713711
}
714712

715-
let newEntry = [sort | context.layerOrder[layer], rule]
713+
let newEntry = [sort, rule]
716714
rules.add(newEntry)
717715
context.ruleCache.add(newEntry)
718716
allRules.push(newEntry)

0 commit comments

Comments
 (0)