Only detect nesting when using @apply (#13325)
* drop nesting detection for `@tailwind` * drop separate nesting detection entirely * detect nesting only when using `@apply` with a class that uses nesting * drop unnecessary `important` config * add test to verify applying nested user CSS errors * add error reason to tests * update `@apply` error message
This commit is contained in:
parent
3ba51d14c7
commit
bda84210d6
5 changed files with 86 additions and 178 deletions
|
|
@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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`<div class="foo"></div>`,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
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`<div class="example"></div>`,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
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"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
import { run, html, css } from './util/run'
|
||||
|
||||
it('should warn when we detect nested css', () => {
|
||||
let config = {
|
||||
content: [{ raw: html`<div class="nested"></div>` }],
|
||||
}
|
||||
|
||||
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`<div class="underline"></div>` }],
|
||||
}
|
||||
|
||||
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`<div class="text-center"></div>` }],
|
||||
}
|
||||
|
||||
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`<div class="nested"></div>` }],
|
||||
}
|
||||
|
||||
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`<div class="nested other"></div>` }],
|
||||
}
|
||||
|
||||
let input = css`
|
||||
@tailwind utilities;
|
||||
|
||||
.nested {
|
||||
.example {
|
||||
}
|
||||
|
||||
.other {
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
.example {
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
return run(input, config).then((result) => {
|
||||
expect(result.messages).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue