From 778a8f40f6fe3bed0d5ee1bd6821a7ef9c0b258e Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 22 Mar 2024 17:32:53 +0100 Subject: [PATCH 1/7] drop nesting detection for `@tailwind` --- src/lib/detectNesting.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/lib/detectNesting.js b/src/lib/detectNesting.js index 03252e2006bb..abbb94cee45d 100644 --- a/src/lib/detectNesting.js +++ b/src/lib/detectNesting.js @@ -10,23 +10,6 @@ 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 From 87338edd6c63b02c810dac6f44932f0a7859bfab Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 22 Mar 2024 18:13:43 +0100 Subject: [PATCH 2/7] drop separate nesting detection entirely --- src/lib/detectNesting.js | 30 -------- src/processTailwindFeatures.js | 3 - tests/detect-nesting.test.js | 128 --------------------------------- 3 files changed, 161 deletions(-) delete mode 100644 src/lib/detectNesting.js delete mode 100644 tests/detect-nesting.test.js diff --git a/src/lib/detectNesting.js b/src/lib/detectNesting.js deleted file mode 100644 index abbb94cee45d..000000000000 --- a/src/lib/detectNesting.js +++ /dev/null @@ -1,30 +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.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/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/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) - }) -}) From ce0ec5adb462c474da34e410a911686a3a104bef Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 22 Mar 2024 19:19:58 +0100 Subject: [PATCH 3/7] detect nesting only when using `@apply` with a class that uses nesting --- src/lib/expandApplyAtRules.js | 17 +++++++++++++++++ tests/apply.test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js index ed48dbc4f75b..4a486cfc7716 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 applied because it uses nested CSS.`, + 'Please enable a CSS nesting plugin *before* Tailwind in your configuration.', + 'See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting', + ].join('\n') + ) + }) + } + candidates.push([applyCandidate, important, rules]) } } diff --git a/tests/apply.test.js b/tests/apply.test.js index f2f7253b3409..eaa670b9e933 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -2143,3 +2143,33 @@ 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 = { + important: '.foo', + content: [ + { + raw: html`
`, + }, + ], + } + + let input = css` + @tailwind components; + @layer components { + .bar .baz { + color: red; + + &:hover { + color: red; + } + } + + .foo { + @apply flex baz; + } + } + ` + + return expect(() => run(input, config)).rejects.toThrowError() +}) From 88845bf3e5b5ad00548d19aa8fed6b0a178cba07 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 22 Mar 2024 21:19:32 +0100 Subject: [PATCH 4/7] drop unnecessary `important` config --- tests/apply.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/apply.test.js b/tests/apply.test.js index eaa670b9e933..348e005a8941 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -2146,7 +2146,6 @@ 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 = { - important: '.foo', content: [ { raw: html`
`, From 5f6a8edb815b14efafd985369cb4248339353256 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Sat, 23 Mar 2024 01:44:03 +0100 Subject: [PATCH 5/7] add test to verify applying nested user CSS errors --- tests/apply.test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/apply.test.js b/tests/apply.test.js index 348e005a8941..510b34dc635d 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -2172,3 +2172,27 @@ test('applying classes with nested CSS should result in an error', async () => { return expect(() => run(input, config)).rejects.toThrowError() }) + +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; + } + ` + + return expect(() => run(input, config)).rejects.toThrowError() +}) From 8dded237a170f316c0cde0eb6353b363c80083fb Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Sat, 23 Mar 2024 01:45:47 +0100 Subject: [PATCH 6/7] add error reason to tests --- tests/apply.test.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/apply.test.js b/tests/apply.test.js index 510b34dc635d..bc5a2eda6df4 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -2170,7 +2170,15 @@ test('applying classes with nested CSS should result in an error', async () => { } ` - return expect(() => run(input, config)).rejects.toThrowError() + expect.assertions(1) + + return run(input, config).catch((err) => { + expect(err.reason).toMatchInlineSnapshot(` + "The \`baz\` class cannot be applied because it uses nested CSS. + Please enable a CSS nesting plugin *before* Tailwind in your configuration. + See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting" + `) + }) }) test('applying user defined classes with nested CSS should result in an error', async () => { @@ -2194,5 +2202,13 @@ test('applying user defined classes with nested CSS should result in an error', } ` - return expect(() => run(input, config)).rejects.toThrowError() + expect.assertions(1) + + return run(input, config).catch((err) => { + expect(err.reason).toMatchInlineSnapshot(` + "The \`bar\` class cannot be applied because it uses nested CSS. + Please enable a CSS nesting plugin *before* Tailwind in your configuration. + See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting" + `) + }) }) From f276cff06bd89d67b1b9b24ca9485c303b2f1d8b Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Sat, 23 Mar 2024 16:00:27 +0100 Subject: [PATCH 7/7] update `@apply` error message --- src/lib/expandApplyAtRules.js | 6 +++--- tests/apply.test.js | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js index 4a486cfc7716..906f9fb2d1e7 100644 --- a/src/lib/expandApplyAtRules.js +++ b/src/lib/expandApplyAtRules.js @@ -441,9 +441,9 @@ function processApply(root, context, localCache) { rule.walkRules(() => { throw apply.error( [ - `The \`${applyCandidate}\` class cannot be applied because it uses nested CSS.`, - 'Please enable a CSS nesting plugin *before* Tailwind in your configuration.', - 'See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting', + `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') ) }) diff --git a/tests/apply.test.js b/tests/apply.test.js index bc5a2eda6df4..627c57404b24 100644 --- a/tests/apply.test.js +++ b/tests/apply.test.js @@ -2174,9 +2174,9 @@ test('applying classes with nested CSS should result in an error', async () => { return run(input, config).catch((err) => { expect(err.reason).toMatchInlineSnapshot(` - "The \`baz\` class cannot be applied because it uses nested CSS. - Please enable a CSS nesting plugin *before* Tailwind in your configuration. - See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting" + "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" `) }) }) @@ -2206,9 +2206,9 @@ test('applying user defined classes with nested CSS should result in an error', return run(input, config).catch((err) => { expect(err.reason).toMatchInlineSnapshot(` - "The \`bar\` class cannot be applied because it uses nested CSS. - Please enable a CSS nesting plugin *before* Tailwind in your configuration. - See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting" + "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" `) }) })