Skip to content

Commit

Permalink
Fix consecutive builds with at apply producing different CSS (#6999)
Browse files Browse the repository at this point in the history
* Partition at rules before building context

* remove unnecessary logic

* Update changelog

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
  • Loading branch information
thecrypticace and RobinMalfait committed Jan 10, 2022
1 parent 3a13cfc commit a891867
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 75 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Nothing yet!
### Fixed

- Fix consecutive builds with at apply producing different CSS ([#6999](https://github.com/tailwindlabs/tailwindcss/pull/6999))

## [3.0.12] - 2022-01-07

Expand Down
52 changes: 52 additions & 0 deletions src/lib/partitionApplyAtRules.js
@@ -0,0 +1,52 @@
function partitionRules(root) {
if (!root.walkAtRules) return

let applyParents = new Set()

root.walkAtRules('apply', (rule) => {
applyParents.add(rule.parent)
})

if (applyParents.size === 0) {
return
}

for (let rule of applyParents) {
let nodeGroups = []
let lastGroup = []

for (let node of rule.nodes) {
if (node.type === 'atrule' && node.name === 'apply') {
if (lastGroup.length > 0) {
nodeGroups.push(lastGroup)
lastGroup = []
}
nodeGroups.push([node])
} else {
lastGroup.push(node)
}
}

if (lastGroup.length > 0) {
nodeGroups.push(lastGroup)
}

if (nodeGroups.length === 1) {
continue
}

for (let group of [...nodeGroups].reverse()) {
let clone = rule.clone({ nodes: [] })
clone.append(group)
rule.after(clone)
}

rule.remove()
}
}

export default function expandApplyAtRules() {
return (root) => {
partitionRules(root)
}
}
79 changes: 5 additions & 74 deletions src/lib/setupContextUtils.js
Expand Up @@ -20,58 +20,6 @@ import log from '../util/log'
import negateValue from '../util/negateValue'
import isValidArbitraryValue from '../util/isValidArbitraryValue'

function partitionRules(root) {
if (!root.walkAtRules) return [root]

let applyParents = new Set()
let rules = []

root.walkAtRules('apply', (rule) => {
applyParents.add(rule.parent)
})

if (applyParents.size === 0) {
rules.push(root)
}

for (let rule of applyParents) {
let nodeGroups = []
let lastGroup = []

for (let node of rule.nodes) {
if (node.type === 'atrule' && node.name === 'apply') {
if (lastGroup.length > 0) {
nodeGroups.push(lastGroup)
lastGroup = []
}
nodeGroups.push([node])
} else {
lastGroup.push(node)
}
}

if (lastGroup.length > 0) {
nodeGroups.push(lastGroup)
}

if (nodeGroups.length === 1) {
rules.push(rule)
continue
}

for (let group of [...nodeGroups].reverse()) {
let clone = rule.clone({ nodes: [] })
clone.append(group)
rules.unshift(clone)
rule.after(clone)
}

rule.remove()
}

return rules
}

function parseVariantFormatString(input) {
if (input.includes('{')) {
if (!isBalanced(input)) throw new Error(`Your { and } are unbalanced.`)
Expand Down Expand Up @@ -284,9 +232,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
context.candidateRuleMap.set(identifier, [])
}

context.candidateRuleMap
.get(identifier)
.push(...partitionRules(rule).map((rule) => [{ sort: offset, layer: 'user' }, rule]))
context.candidateRuleMap.get(identifier).push([{ sort: offset, layer: 'user' }, rule])
}
},
addBase(base) {
Expand All @@ -300,7 +246,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs

context.candidateRuleMap
.get(prefixedIdentifier)
.push(...partitionRules(rule).map((rule) => [{ sort: offset, layer: 'base' }, rule]))
.push([{ sort: offset, layer: 'base' }, rule])
}
},
/**
Expand All @@ -321,12 +267,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs

context.candidateRuleMap
.get(prefixedIdentifier)
.push(
...partitionRules(rule).map((rule) => [
{ sort: offsets.base++, layer: 'defaults' },
rule,
])
)
.push([{ sort: offsets.base++, layer: 'defaults' }, rule])
}
},
addComponents(components, options) {
Expand All @@ -348,12 +289,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs

context.candidateRuleMap
.get(prefixedIdentifier)
.push(
...partitionRules(rule).map((rule) => [
{ sort: offsets.components++, layer: 'components', options },
rule,
])
)
.push([{ sort: offsets.components++, layer: 'components', options }, rule])
}
},
addUtilities(utilities, options) {
Expand All @@ -375,12 +311,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs

context.candidateRuleMap
.get(prefixedIdentifier)
.push(
...partitionRules(rule).map((rule) => [
{ sort: offsets.utilities++, layer: 'utilities', options },
rule,
])
)
.push([{ sort: offsets.utilities++, layer: 'utilities', options }, rule])
}
},
matchUtilities: function (utilities, options) {
Expand Down
2 changes: 2 additions & 0 deletions src/processTailwindFeatures.js
Expand Up @@ -6,6 +6,7 @@ import substituteScreenAtRules from './lib/substituteScreenAtRules'
import resolveDefaultsAtRules from './lib/resolveDefaultsAtRules'
import collapseAdjacentRules from './lib/collapseAdjacentRules'
import collapseDuplicateDeclarations from './lib/collapseDuplicateDeclarations'
import partitionApplyAtRules from './lib/partitionApplyAtRules'
import detectNesting from './lib/detectNesting'
import { createContext } from './lib/setupContextUtils'
import { issueFlagNotices } from './featureFlags'
Expand All @@ -15,6 +16,7 @@ export default function processTailwindFeatures(setupContext) {
let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root)

detectNesting()(root, result)
partitionApplyAtRules()(root, result)

let context = setupContext({
tailwindDirectives,
Expand Down
63 changes: 63 additions & 0 deletions tests/apply.test.js
Expand Up @@ -1139,3 +1139,66 @@ it('apply does not emit defaults in isolated environments without optimizeUniver
`)
})
})

it('should work outside of layer', async () => {
let config = {
content: [{ raw: html`<div class="input-text"></div>` }],
corePlugins: { preflight: false },
}

let input = css`
.input-text {
@apply bg-white;
background-color: red;
}
`

let result
result = await run(input, config)

expect(result.css).toMatchFormattedCss(css`
.input-text {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
background-color: red;
}
`)

result = await run(input, config)

expect(result.css).toMatchFormattedCss(css`
.input-text {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
background-color: red;
}
`)
})

it('should work in layer', async () => {
let config = {
content: [{ raw: html`<div class="input-text"></div>` }],
corePlugins: { preflight: false },
}

let input = css`
@tailwind components;
@layer components {
.input-text {
@apply bg-white;
background-color: red;
}
}
`

await run(input, config)
const result = await run(input, config)

expect(result.css).toMatchFormattedCss(css`
.input-text {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
background-color: red;
}
`)
})

0 comments on commit a891867

Please sign in to comment.