From b69e46cc1b32608d779dad35121077b48089485d Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Wed, 15 Jul 2020 09:25:22 -0400 Subject: [PATCH] Rename buckets to layers, handle copying layer information to responsive variants --- src/lib/purgeUnusedStyles.js | 62 ++++++++++++++++---------- src/lib/substituteResponsiveAtRules.js | 54 +++++++++++++++++++++- src/processTailwindFeatures.js | 9 ---- src/util/processPlugins.js | 10 ++--- 4 files changed, 96 insertions(+), 39 deletions(-) diff --git a/src/lib/purgeUnusedStyles.js b/src/lib/purgeUnusedStyles.js index a0192fe48..a006a9e3a 100644 --- a/src/lib/purgeUnusedStyles.js +++ b/src/lib/purgeUnusedStyles.js @@ -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 => { diff --git a/src/lib/substituteResponsiveAtRules.js b/src/lib/substituteResponsiveAtRules.js index 7436d323e..debc18f67 100644 --- a/src/lib/substituteResponsiveAtRules.js +++ b/src/lib/substituteResponsiveAtRules.js @@ -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() }) diff --git a/src/processTailwindFeatures.js b/src/processTailwindFeatures.js index a87a1e8c8..294a8c5d7 100644 --- a/src/processTailwindFeatures.js +++ b/src/processTailwindFeatures.js @@ -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') }) } diff --git a/src/util/processPlugins.js b/src/util/processPlugins.js index 088024f96..63ed9cbac 100644 --- a/src/util/processPlugins.js +++ b/src/util/processPlugins.js @@ -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 => {