From 5777c278633c4e9cb67f31e3bc250e216bf38520 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Fri, 13 May 2022 17:19:46 -0400 Subject: [PATCH 1/4] WIP Still need to write error message --- src/lib/generateRules.js | 6 +++++- src/lib/setupContextUtils.js | 8 ++++++++ tests/arbitrary-variants.test.js | 28 ++++++++++++++++++++++++++++ tests/variants.test.js | 19 +++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index a14f7163a076..ccba747d9020 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -9,7 +9,7 @@ import * as sharedState from './sharedState' import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector' import { asClass } from '../util/nameClass' import { normalize } from '../util/dataTypes' -import { parseVariant } from './setupContextUtils' +import { isValidVariantFormatString, parseVariant } from './setupContextUtils' import isValidArbitraryValue from '../util/isValidArbitraryValue' import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js' @@ -131,6 +131,10 @@ function applyVariant(variant, matches, context) { if (isArbitraryValue(variant) && !context.variantMap.has(variant)) { let selector = normalize(variant.slice(1, -1)) + if (!isValidVariantFormatString(selector)) { + return [] + } + let fn = parseVariant(selector) let sort = Array.from(context.variantOrder.values()).pop() << 1n diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 573341a6c488..cedd0fbf0163 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -170,6 +170,10 @@ function withIdentifiers(styles) { }) } +export function isValidVariantFormatString(format) { + return format.startsWith('@') || format.includes('&') +} + export function parseVariant(variant) { variant = variant .replace(/\n+/g, '') @@ -225,6 +229,10 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs } } + if (!isValidVariantFormatString(variantFunction)) { + throw new Error("you're holding it wrong") + } + return parseVariant(variantFunction) }) diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index 45dbf70c5a34..ffa4a70732d4 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -77,6 +77,34 @@ test('arbitrary variants with modifiers', () => { }) }) +test('variants without & or an at-rule are ignored', () => { + let config = { + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(input, config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + ${defaults} + `) + }) +}) + test('arbitrary variants are sorted after other variants', () => { let config = { content: [{ raw: html`
` }], diff --git a/tests/variants.test.js b/tests/variants.test.js index 2689c06cde01..6d399ec52dcf 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -206,6 +206,25 @@ describe('custom advanced variants', () => { `) }) }) + + test('variant format string must include at-rule or &', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('wtf-bbq', 'lol') + }, + ], + } + + await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( + "you're holding it wrong" + ) + }) }) test('stacked peer variants', async () => { From dc98fd6c1b8df6cd8fdb6ff8af1b6bc82fdce4d2 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 13 May 2022 17:32:11 -0400 Subject: [PATCH 2/4] Update error message first pass at something better --- src/lib/setupContextUtils.js | 2 +- tests/variants.test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index cedd0fbf0163..000fe50b063a 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -230,7 +230,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs } if (!isValidVariantFormatString(variantFunction)) { - throw new Error("you're holding it wrong") + throw new Error("Custom variants must use a media query or provide an `&` to specify selector placement.") } return parseVariant(variantFunction) diff --git a/tests/variants.test.js b/tests/variants.test.js index 6d399ec52dcf..f03fabc976c1 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -207,7 +207,7 @@ describe('custom advanced variants', () => { }) }) - test('variant format string must include at-rule or &', async () => { + test('variant format string must include at-rule or & (1)', async () => { let config = { content: [ { @@ -222,7 +222,7 @@ describe('custom advanced variants', () => { } await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( - "you're holding it wrong" + "Custom variants must use a media query or provide an `&` to specify selector placement." ) }) }) From 6ca9569327608a81a6709381268d67f4e023845b Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 13 May 2022 17:32:48 -0400 Subject: [PATCH 3/4] Detect invalid variant formats returned by functions --- src/lib/setupContextUtils.js | 8 +++++++- tests/variants.test.js | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 000fe50b063a..88d32a4f0e85 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -225,7 +225,13 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs if (typeof variantFunction !== 'string') { // Safelist public API functions return ({ modifySelectors, container, separator }) => { - return variantFunction({ modifySelectors, container, separator }) + let result = variantFunction({ modifySelectors, container, separator }) + + if (typeof result === 'string' && !isValidVariantFormatString(result)) { + throw new Error("Custom variants must use a media query or provide an `&` to specify selector placement.") + } + + return result } } diff --git a/tests/variants.test.js b/tests/variants.test.js index f03fabc976c1..3a7d4fc376c7 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -225,6 +225,25 @@ describe('custom advanced variants', () => { "Custom variants must use a media query or provide an `&` to specify selector placement." ) }) + + test('variant format string must include at-rule or & (2)', async () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('wtf-bbq', () => 'lol') + }, + ], + } + + await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( + "Custom variants must use a media query or provide an `&` to specify selector placement." + ) + }) }) test('stacked peer variants', async () => { From f2d02c30bd366ae98fec1431a47992854dd284f3 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Fri, 13 May 2022 19:29:01 -0400 Subject: [PATCH 4/4] Add proper error message --- src/lib/setupContextUtils.js | 8 ++++++-- tests/variants.test.js | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 88d32a4f0e85..488b8f975307 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -228,7 +228,9 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs let result = variantFunction({ modifySelectors, container, separator }) if (typeof result === 'string' && !isValidVariantFormatString(result)) { - throw new Error("Custom variants must use a media query or provide an `&` to specify selector placement.") + throw new Error( + `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.` + ) } return result @@ -236,7 +238,9 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs } if (!isValidVariantFormatString(variantFunction)) { - throw new Error("Custom variants must use a media query or provide an `&` to specify selector placement.") + throw new Error( + `Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.` + ) } return parseVariant(variantFunction) diff --git a/tests/variants.test.js b/tests/variants.test.js index 3a7d4fc376c7..c3031fd11d67 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -222,7 +222,7 @@ describe('custom advanced variants', () => { } await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( - "Custom variants must use a media query or provide an `&` to specify selector placement." + "Your custom variant `wtf-bbq` has an invalid format string. Make sure it's an at-rule or contains a `&` placeholder." ) }) @@ -241,7 +241,7 @@ describe('custom advanced variants', () => { } await expect(run('@tailwind components;@tailwind utilities', config)).rejects.toThrowError( - "Custom variants must use a media query or provide an `&` to specify selector placement." + "Your custom variant `wtf-bbq` has an invalid format string. Make sure it's an at-rule or contains a `&` placeholder." ) }) })