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

Only emit utility/component variants when those layers exist #7066

Merged
merged 1 commit into from Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Show warnings for invalid content config ([#7065](https://github.com/tailwindlabs/tailwindcss/pull/7065))

### Fixed

- Only emit utility/component variants when those layers exist ([#7066](https://github.com/tailwindlabs/tailwindcss/pull/7066))

## [3.0.13] - 2022-01-11

### Fixed
Expand Down
31 changes: 23 additions & 8 deletions src/lib/expandTailwindAtRules.js
Expand Up @@ -205,9 +205,6 @@ export default function expandTailwindAtRules(context) {

if (layerNodes.base) {
layerNodes.base.before(cloneNodes([...baseNodes, ...defaultNodes], layerNodes.base.source))
}

if (layerNodes.base) {
layerNodes.base.remove()
}

Expand All @@ -221,16 +218,34 @@ export default function expandTailwindAtRules(context) {
layerNodes.utilities.remove()
}

// We do post-filtering to not alter the emitted order of the variants
const variantNodes = Array.from(screenNodes).filter((node) => {
const parentLayer = node.raws.tailwind?.parentLayer

if (parentLayer === 'components') {
return layerNodes.components !== null
}

if (parentLayer === 'utilities') {
return layerNodes.utilities !== null
}

return true
})

if (layerNodes.variants) {
layerNodes.variants.before(cloneNodes([...screenNodes], layerNodes.variants.source))
layerNodes.variants.before(cloneNodes(variantNodes, layerNodes.variants.source))
layerNodes.variants.remove()
} else {
root.append(cloneNodes([...screenNodes], root.source))
} else if (variantNodes.length > 0) {
root.append(cloneNodes(variantNodes, root.source))
}

// If we've got a utility layer and no utilities are generated there's likely something wrong
// TODO: Detect utility variants only
if (layerNodes.utilities && utilityNodes.size === 0 && screenNodes.size === 0) {
const hasUtilityVariants = variantNodes.some(
(node) => node.raws.tailwind?.parentLayer === 'utilities'
)

if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) {
log.warn('content-problems', [
'No utilities were generated there is likely a problem with the `content` key in the tailwind config. For more information see the documentation: https://tailwindcss.com/docs/content-configuration',
])
Expand Down
6 changes: 6 additions & 0 deletions src/lib/generateRules.js
Expand Up @@ -216,6 +216,12 @@ function applyVariant(variant, matches, context) {
})
}

// This tracks the originating layer for the variant
// For example:
// .sm:underline {} is a variant of something in the utilities layer
// .sm:container {} is a variant of the container component
clone.nodes[0].raws.tailwind = { parentLayer: meta.layer }

let withOffset = [
{
...meta,
Expand Down
74 changes: 73 additions & 1 deletion tests/variants.test.js
@@ -1,7 +1,7 @@
import fs from 'fs'
import path from 'path'

import { run, css, html } from './util/run'
import { run, css, html, defaults } from './util/run'

test('variants', () => {
let config = {
Expand Down Expand Up @@ -467,3 +467,75 @@ it('should be possible to use responsive modifiers that are defined with special
`)
})
})

it('including just the base layer should not produce variants', () => {
let config = {
content: [{ raw: html`<div class="sm:container sm:underline"></div>` }],
corePlugins: { preflight: false },
}

return run('@tailwind base', config).then((result) => {
return expect(result.css).toMatchFormattedCss(
css`
${defaults}
`
)
})
})

it('variants for components should not be produced in a file without a components layer', () => {
let config = {
content: [{ raw: html`<div class="sm:container sm:underline"></div>` }],
}

return run('@tailwind utilities', config).then((result) => {
return expect(result.css).toMatchFormattedCss(css`
@media (min-width: 640px) {
.sm\:underline {
text-decoration-line: underline;
}
}
`)
})
})

it('variants for utilities should not be produced in a file without a utilities layer', () => {
let config = {
content: [{ raw: html`<div class="sm:container sm:underline"></div>` }],
}

return run('@tailwind components', config).then((result) => {
return expect(result.css).toMatchFormattedCss(css`
@media (min-width: 640px) {
.sm\:container {
width: 100%;
}
@media (min-width: 640px) {
.sm\:container {
max-width: 640px;
}
}
@media (min-width: 768px) {
.sm\:container {
max-width: 768px;
}
}
@media (min-width: 1024px) {
.sm\:container {
max-width: 1024px;
}
}
@media (min-width: 1280px) {
.sm\:container {
max-width: 1280px;
}
}
@media (min-width: 1536px) {
.sm\:container {
max-width: 1536px;
}
}
}
`)
})
})