diff --git a/src/lib/detectNesting.js b/src/lib/detectNesting.js deleted file mode 100644 index 03252e2006bb..000000000000 --- a/src/lib/detectNesting.js +++ /dev/null @@ -1,47 +0,0 @@ -function isRoot(node) { - return node.type === 'root' -} - -function isAtLayer(node) { - return node.type === 'atrule' && node.name === 'layer' -} - -export default function (_context) { - return (root, result) => { - let found = false - - root.walkAtRules('tailwind', (node) => { - if (found) return false - - if (node.parent && !(isRoot(node.parent) || isAtLayer(node.parent))) { - found = true - node.warn( - result, - [ - 'Nested @tailwind rules were detected, but are not supported.', - "Consider using a prefix to scope Tailwind's classes: https://tailwindcss.com/docs/configuration#prefix", - 'Alternatively, use the important selector strategy: https://tailwindcss.com/docs/configuration#selector-strategy', - ].join('\n') - ) - return false - } - }) - - root.walkRules((rule) => { - if (found) return false - - rule.walkRules((nestedRule) => { - found = true - nestedRule.warn( - result, - [ - 'Nested CSS was detected, but CSS nesting has not been configured correctly.', - 'Please enable a CSS nesting plugin *before* Tailwind in your configuration.', - 'See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting', - ].join('\n') - ) - return false - }) - }) - } -} diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js index ed48dbc4f75b..906f9fb2d1e7 100644 --- a/src/lib/expandApplyAtRules.js +++ b/src/lib/expandApplyAtRules.js @@ -432,6 +432,23 @@ function processApply(root, context, localCache) { let rules = applyClassCache.get(applyCandidate) + // Verify that we can apply the class + for (let [, rule] of rules) { + if (rule.type === 'atrule') { + continue + } + + rule.walkRules(() => { + throw apply.error( + [ + `The \`${applyCandidate}\` class cannot be used with \`@apply\` because \`@apply\` does not currently support nested CSS.`, + 'Rewrite the selector without nesting or configure the `tailwindcss/nesting` plugin:', + 'https://tailwindcss.com/docs/using-with-preprocessors#nesting', + ].join('\n') + ) + }) + } + candidates.push([applyCandidate, important, rules]) } } diff --git a/src/processTailwindFeatures.js b/src/processTailwindFeatures.js index fa363b003bf2..2ab36229dfc5 100644 --- a/src/processTailwindFeatures.js +++ b/src/processTailwindFeatures.js @@ -7,7 +7,6 @@ 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' @@ -15,8 +14,6 @@ export default function processTailwindFeatures(setupContext) { return async function (root, result) { let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root) - detectNesting()(root, result) - // Partition apply rules that are found in the css // itself. partitionApplyAtRules()(root, result) diff --git a/tests/apply.test.js b/tests/apply.test.js index f2f7253b3409..627c57404b24 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -2143,3 +2143,72 @@ test('should not break replacing important selector when the same as the parent } `) }) + +test('applying classes with nested CSS should result in an error', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } + + let input = css` + @tailwind components; + @layer components { + .bar .baz { + color: red; + + &:hover { + color: red; + } + } + + .foo { + @apply flex baz; + } + } + ` + + expect.assertions(1) + + return run(input, config).catch((err) => { + expect(err.reason).toMatchInlineSnapshot(` + "The \`baz\` class cannot be used with \`@apply\` because \`@apply\` does not currently support nested CSS. + Rewrite the selector without nesting or configure the \`tailwindcss/nesting\` plugin: + https://tailwindcss.com/docs/using-with-preprocessors#nesting" + `) + }) +}) + +test('applying user defined classes with nested CSS should result in an error', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + } + + let input = css` + .foo { + .bar { + color: red; + } + } + + .example { + @apply bar; + } + ` + + expect.assertions(1) + + return run(input, config).catch((err) => { + expect(err.reason).toMatchInlineSnapshot(` + "The \`bar\` class cannot be used with \`@apply\` because \`@apply\` does not currently support nested CSS. + Rewrite the selector without nesting or configure the \`tailwindcss/nesting\` plugin: + https://tailwindcss.com/docs/using-with-preprocessors#nesting" + `) + }) +}) diff --git a/tests/detect-nesting.test.js b/tests/detect-nesting.test.js deleted file mode 100644 index 08eeacf98464..000000000000 --- a/tests/detect-nesting.test.js +++ /dev/null @@ -1,128 +0,0 @@ -import { run, html, css } from './util/run' - -it('should warn when we detect nested css', () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind utilities; - - .nested { - .example { - } - } - ` - - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(1) - expect(result.messages).toMatchObject([ - { - type: 'warning', - text: [ - 'Nested CSS was detected, but CSS nesting has not been configured correctly.', - 'Please enable a CSS nesting plugin *before* Tailwind in your configuration.', - 'See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting', - ].join('\n'), - }, - ]) - }) -}) - -it('should not warn when we detect nested css inside css @layer rules', () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - @layer tw-base, tw-components, tw-utilities; - @layer tw-utilities { - @tailwind utilities; - } - ` - - return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - @layer tw-base, tw-components, tw-utilities; - @layer tw-utilities { - .underline { - text-decoration-line: underline; - } - } - `) - expect(result.messages).toHaveLength(0) - }) -}) - -it('should warn when we detect namespaced @tailwind at rules', () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - .namespace { - @tailwind utilities; - } - ` - - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(1) - expect(result.messages).toMatchObject([ - { - type: 'warning', - text: [ - 'Nested @tailwind rules were detected, but are not supported.', - "Consider using a prefix to scope Tailwind's classes: https://tailwindcss.com/docs/configuration#prefix", - 'Alternatively, use the important selector strategy: https://tailwindcss.com/docs/configuration#selector-strategy', - ].join('\n'), - }, - ]) - }) -}) - -it('should not warn when nesting a single rule inside a media query', () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind utilities; - - @media (min-width: 768px) { - .nested { - } - } - ` - - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(0) - expect(result.messages).toEqual([]) - }) -}) - -it('should only warn for the first detected nesting ', () => { - let config = { - content: [{ raw: html`
` }], - } - - let input = css` - @tailwind utilities; - - .nested { - .example { - } - - .other { - } - } - - .other { - .example { - } - } - ` - - return run(input, config).then((result) => { - expect(result.messages).toHaveLength(1) - }) -})