Skip to content

Commit 7572b02

Browse files
feat: Show "Did you mean?" message when users @apply errant class (#2590)
* feat: suggest alternate classes if @apply value not found * fix: remove only from test * feat: move logic to applyComplexClasses * fix: remove whitespace * remove unused files * did you mean suggestion
1 parent 3141425 commit 7572b02

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

__tests__/applyAtRule.test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,50 @@ describe('using apply with the prefix option', () => {
887887
})
888888
})
889889

890+
test('a "Did You Mean" suggestion is recommended if a similar class can be identified.', () => {
891+
const input = `
892+
.foo { @apply anti-aliased; }
893+
`
894+
895+
const config = resolveConfig([
896+
{
897+
...defaultConfig,
898+
},
899+
])
900+
901+
expect.assertions(1)
902+
903+
return run(input, config).catch((e) => {
904+
expect(e).toMatchObject({
905+
name: 'CssSyntaxError',
906+
reason:
907+
"The `anti-aliased` class does not exist, but `antialiased` does. If you're sure that `anti-aliased` exists, make sure that any `@import` statements are being properly processed before Tailwind CSS sees your CSS, as `@apply` can only be used for classes in the same CSS tree.",
908+
})
909+
})
910+
})
911+
912+
test('a "Did You Mean" suggestion is omitted if a similar class cannot be identified.', () => {
913+
const input = `
914+
.foo { @apply anteater; }
915+
`
916+
917+
const config = resolveConfig([
918+
{
919+
...defaultConfig,
920+
},
921+
])
922+
923+
expect.assertions(1)
924+
925+
return run(input, config).catch((e) => {
926+
expect(e).toMatchObject({
927+
name: 'CssSyntaxError',
928+
reason:
929+
"The `anteater` class does not exist. If you're sure that `anteater` exists, make sure that any `@import` statements are being properly processed before Tailwind CSS sees your CSS, as `@apply` can only be used for classes in the same CSS tree.",
930+
})
931+
})
932+
})
933+
890934
test('you can apply classes with important and a prefix enabled', () => {
891935
const input = `
892936
.foo { @apply tw-mt-4; }

src/lib/substituteClassApplyAtRules.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import _ from 'lodash'
22
import selectorParser from 'postcss-selector-parser'
33
import postcss from 'postcss'
4+
import didYouMean from 'didyoumean'
45
import substituteTailwindAtRules from './substituteTailwindAtRules'
56
import evaluateTailwindFunctions from './evaluateTailwindFunctions'
67
import substituteVariantsAtRules from './substituteVariantsAtRules'
@@ -166,6 +167,8 @@ function makeExtractUtilityRules(css, lookupTree, config) {
166167
if (utilityMap[utilityName] === undefined) {
167168
// Look for prefixed utility in case the user has goofed
168169
const prefixedUtility = prefixSelector(config.prefix, `.${utilityName}`).slice(1)
170+
const suggestedClass = didYouMean(utilityName, Object.keys(utilityMap))
171+
const suggestionMessage = suggestedClass ? `, but \`${suggestedClass}\` does` : ''
169172

170173
if (utilityMap[prefixedUtility] !== undefined) {
171174
throw rule.error(
@@ -174,7 +177,7 @@ function makeExtractUtilityRules(css, lookupTree, config) {
174177
}
175178

176179
throw rule.error(
177-
`The \`${utilityName}\` class does not exist. If you're sure that \`${utilityName}\` exists, make sure that any \`@import\` statements are being properly processed before Tailwind CSS sees your CSS, as \`@apply\` can only be used for classes in the same CSS tree.`,
180+
`The \`${utilityName}\` class does not exist${suggestionMessage}. If you're sure that \`${utilityName}\` exists, make sure that any \`@import\` statements are being properly processed before Tailwind CSS sees your CSS, as \`@apply\` can only be used for classes in the same CSS tree.`,
178181
{ word: utilityName }
179182
)
180183
}

0 commit comments

Comments
 (0)