Rename buckets to layers, handle copying layer information to responsive variants

This commit is contained in:
Adam Wathan 2020-07-15 09:25:22 -04:00
parent f25b0925a3
commit b69e46cc1b
4 changed files with 96 additions and 39 deletions

View file

@ -5,6 +5,40 @@ import chalk from 'chalk'
import { log } from '../cli/utils'
import * as emoji from '../cli/emoji'
function convertLayersToControlComments(css) {
css.walkAtRules('layer', atRule => {
const layer = atRule.params
atRule.before(postcss.comment({ text: `tailwind start ${layer}` }))
atRule.before(atRule.nodes)
atRule.before(postcss.comment({ text: `tailwind end ${layer}` }))
atRule.remove()
})
}
function convertControlCommentsToPurgeIgnoreComments(config) {
return function(css) {
const mode = _.get(config, 'purge.mode', 'conservative')
if (mode === 'conservative') {
css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
css.append(postcss.comment({ text: 'purgecss end ignore' }))
css.walkComments(comment => {
switch (comment.text.trim()) {
case 'tailwind start utilities':
comment.text = 'purgecss end ignore'
break
case 'tailwind end utilities':
comment.text = 'purgecss start ignore'
break
default:
break
}
})
}
}
}
function removeTailwindComments(css) {
css.walkComments(comment => {
switch (comment.text.trim()) {
@ -28,7 +62,7 @@ export default function purgeUnusedUtilities(config) {
)
if (!purgeEnabled) {
return removeTailwindComments
return postcss([convertLayersToControlComments, removeTailwindComments])
}
// Skip if `purge: []` since that's part of the default config
@ -52,29 +86,9 @@ export default function purgeUnusedUtilities(config) {
}
return postcss([
function(css) {
const mode = _.get(config, 'purge.mode', 'conservative')
if (mode === 'conservative') {
css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
css.append(postcss.comment({ text: 'purgecss end ignore' }))
css.walkComments(comment => {
switch (comment.text.trim()) {
case 'tailwind start utilities':
comment.text = 'purgecss end ignore'
break
case 'tailwind end utilities':
comment.text = 'purgecss start ignore'
break
default:
break
}
})
}
removeTailwindComments(css)
},
convertLayersToControlComments,
convertControlCommentsToPurgeIgnoreComments(config),
removeTailwindComments,
purgecss({
content: Array.isArray(config.purge) ? config.purge : config.purge.content,
defaultExtractor: content => {

View file

@ -4,8 +4,51 @@ import cloneNodes from '../util/cloneNodes'
import buildMediaQuery from '../util/buildMediaQuery'
import buildSelectorVariant from '../util/buildSelectorVariant'
function isLayer(node) {
if (Array.isArray(node)) {
return node.length === 1 && isLayer(node[0])
}
return node.type === 'atrule' && node.name === 'layer'
}
function layerNodes(nodes) {
return isLayer(nodes) ? nodes[0].nodes : nodes
}
export default function(config) {
return function(css) {
// Wrap any `responsive` rules with a copy of their parent `layer` to
// ensure the layer isn't lost when copying to the `screens` location.
css.walkAtRules('layer', layerAtRule => {
const layer = layerAtRule.params
layerAtRule.walkAtRules('responsive', responsiveAtRule => {
const nestedlayerAtRule = postcss.atRule({
name: 'layer',
params: layer,
})
nestedlayerAtRule.prepend(responsiveAtRule.nodes)
responsiveAtRule.removeAll()
responsiveAtRule.prepend(nestedlayerAtRule)
})
})
// Find any `responsive` rules with no parent `layer` (these only come
// from the user's CSS, Tailwind never generates things this way itself)
// and wrap them with a default `layer` of `utilities`.
css.walkAtRules('responsive', responsiveAtRule => {
const [node] = responsiveAtRule.nodes
if (node.type === 'atrule' && node.name === 'layer') {
return
}
const nestedlayerAtRule = postcss.atRule({
name: 'layer',
params: 'utilities',
})
nestedlayerAtRule.prepend(responsiveAtRule.nodes)
responsiveAtRule.removeAll()
responsiveAtRule.prepend(nestedlayerAtRule)
})
const {
theme: { screens },
separator,
@ -16,7 +59,16 @@ export default function(config) {
css.walkAtRules('responsive', atRule => {
const nodes = atRule.nodes
responsiveRules.append(...cloneNodes(nodes))
atRule.before(nodes)
// If the parent is already a `layer` (this is true for anything coming from
// a plugin, including core plugins) we don't want to create a double nested
// layer, so only insert the layer children. If there is no parent layer,
// preserve the layer information when inserting the nodes.
if (isLayer(atRule.parent)) {
atRule.before(layerNodes(nodes))
} else {
atRule.before(nodes)
}
atRule.remove()
})

View file

@ -24,15 +24,6 @@ export default function(getConfig) {
substituteResponsiveAtRules(config),
substituteScreenAtRules(config),
substituteClassApplyAtRules(config, processedPlugins.utilities),
function(css) {
css.walkAtRules('bucket', atRule => {
const bucket = atRule.params
atRule.before(postcss.comment({ text: `tailwind start ${bucket}` }))
atRule.before(atRule.nodes)
atRule.before(postcss.comment({ text: `tailwind end ${bucket}` }))
atRule.remove()
})
},
purgeUnusedStyles(config),
]).process(css, { from: _.get(css, 'source.input.file') })
}

View file

@ -28,11 +28,11 @@ function containsClass(value) {
}).transformSync(value)
}
function wrapWithBucket(rules, bucket) {
function wrapWithLayer(rules, layer) {
return postcss
.atRule({
name: 'bucket',
params: bucket,
name: 'layer',
params: layer,
})
.append(cloneNodes(Array.isArray(rules) ? rules : [rules]))
}
@ -119,7 +119,7 @@ export default function(plugins, config) {
})
pluginUtilities.push(
wrapWithVariants(wrapWithBucket(styles.nodes, 'utilities'), options.variants)
wrapWithLayer(wrapWithVariants(styles.nodes, options.variants), 'utilities')
)
},
addComponents: (components, options) => {
@ -138,7 +138,7 @@ export default function(plugins, config) {
})
pluginComponents.push(
wrapWithVariants(wrapWithBucket(styles.nodes, 'components'), options.variants)
wrapWithLayer(wrapWithVariants(styles.nodes, options.variants), 'components')
)
},
addBase: baseStyles => {