Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parallel variant ordering clash #9282

Merged
merged 23 commits into from Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 5 additions & 11 deletions src/lib/expandApplyAtRules.js
Expand Up @@ -2,7 +2,6 @@ import postcss from 'postcss'
import parser from 'postcss-selector-parser'

import { resolveMatches } from './generateRules'
import bigSign from '../util/bigSign'
import escapeClassName from '../util/escapeClassName'

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

let highestOffset = context.layerOrder.user >> 4n

root.walkRules((rule, idx) => {
root.walkRules((rule) => {
// Ignore rules generated by Tailwind
for (let node of pathToRoot(rule)) {
if (node.raws.tailwind?.layer !== undefined) {
Expand All @@ -161,6 +158,7 @@ function buildLocalApplyCache(root, context) {

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

for (let className of extractClasses(rule)) {
let list = cache.get(className) || []
Expand All @@ -169,7 +167,7 @@ function buildLocalApplyCache(root, context) {
list.push([
{
layer: 'user',
sort: BigInt(idx) + highestOffset,
sort,
important: false,
},
container,
Expand Down Expand Up @@ -553,16 +551,12 @@ function processApply(root, context, localCache) {
}

// Insert it
siblings.push([
// Ensure that when we are sorting, that we take the layer order into account
{ ...meta, sort: meta.sort | context.layerOrder[meta.layer] },
root.nodes[0],
])
siblings.push([meta.sort, root.nodes[0]])
}
}

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

// `parent` refers to the node at `.abc` in: .abc { @apply mt-2 }
parent.after(nodes)
Expand Down
48 changes: 7 additions & 41 deletions src/lib/expandTailwindAtRules.js
@@ -1,7 +1,6 @@
import LRU from 'quick-lru'
import * as sharedState from './sharedState'
import { generateRules } from './generateRules'
import bigSign from '../util/bigSign'
import log from '../util/log'
import cloneNodes from '../util/cloneNodes'
import { defaultExtractor } from './defaultExtractor'
Expand Down Expand Up @@ -74,57 +73,24 @@ function getClassCandidates(content, extractor, candidates, seen) {
}
}

/**
*
* @param {[import('./offsets.js').RuleOffset, import('postcss').Node][]} rules
* @param {*} context
*/
function buildStylesheet(rules, context) {
let sortedRules = rules.sort(([a], [z]) => bigSign(a - z))
let sortedRules = context.offsets.sort(rules)

let returnValue = {
base: new Set(),
defaults: new Set(),
components: new Set(),
utilities: new Set(),
variants: new Set(),

// All the CSS that is not Tailwind related can be put in this bucket. This
// will make it easier to later use this information when we want to
// `@apply` for example. The main reason we do this here is because we
// still need to make sure the order is correct. Last but not least, we
// will make sure to always re-inject this section into the css, even if
// certain rules were not used. This means that it will look like a no-op
// from the user's perspective, but we gathered all the useful information
// we need.
user: new Set(),
}

for (let [sort, rule] of sortedRules) {
if (sort >= context.minimumScreen) {
returnValue.variants.add(rule)
continue
}

if (sort & context.layerOrder.base) {
returnValue.base.add(rule)
continue
}

if (sort & context.layerOrder.defaults) {
returnValue.defaults.add(rule)
continue
}

if (sort & context.layerOrder.components) {
returnValue.components.add(rule)
continue
}

if (sort & context.layerOrder.utilities) {
returnValue.utilities.add(rule)
continue
}

if (sort & context.layerOrder.user) {
returnValue.user.add(rule)
continue
}
returnValue[sort.layer].add(rule)
}

return returnValue
Expand Down
20 changes: 9 additions & 11 deletions src/lib/generateRules.js
Expand Up @@ -146,9 +146,9 @@ function applyVariant(variant, matches, context) {

let fn = parseVariant(selector)

let sort = Array.from(context.variantOrder.values()).pop() << 1n
let sort = context.offsets.recordVariant(variant)

context.variantMap.set(variant, [[sort, fn]])
context.variantOrder.set(variant, sort)
}

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

// If the clone has been modified we have to pass that back
Expand Down Expand Up @@ -298,7 +294,7 @@ function applyVariant(variant, matches, context) {
let withOffset = [
{
...meta,
sort: variantSort | meta.sort,
sort: context.offsets.applyVariantOffset(meta.sort, variantSort),
collectedFormats: (meta.collectedFormats ?? []).concat(collectedFormats),
isArbitraryVariant: isArbitraryValue(variant),
},
Expand Down Expand Up @@ -409,9 +405,11 @@ function extractArbitraryProperty(classCandidate, context) {
return null
}

let sort = context.offsets.arbitraryProperty()

return [
[
{ sort: context.arbitraryPropertiesSort, layer: 'utilities' },
{ sort, layer: 'utilities' },
() => ({
[asClass(classCandidate)]: {
[property]: normalized,
Expand Down Expand Up @@ -704,15 +702,15 @@ function generateRules(candidates, context) {
context.candidateRuleCache.set(candidate, rules)

for (const match of matches) {
let [{ sort, layer, options }, rule] = match
let [{ sort, options }, rule] = match

if (options.respectImportant && strategy) {
let container = postcss.root({ nodes: [rule.clone()] })
container.walkRules(strategy)
rule = container.nodes[0]
}

let newEntry = [sort | context.layerOrder[layer], rule]
let newEntry = [sort, rule]
rules.add(newEntry)
context.ruleCache.add(newEntry)
allRules.push(newEntry)
Expand Down