From 905e0a1c590eee0cefc81ab9bc4a86a354a51c46 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Thu, 17 Nov 2022 17:46:13 +0100 Subject: [PATCH 1/2] fix `foo-[abc]/[def]` not being handled correctly This commit does a bit of cleanup, it also ensures that we lookup `[abc]/[def]` in the `values` first, and if it doesn't exist, then we start parsing all the values out. We also ensure that `abc` and `def` are parsed out correctly for the correct type instead of dropping the rule altogether because we happen to end up with an `any` rule. TODO: we should further clean the whole type system because this should only be used to figure out what type an arbitrary value is and to find the corresponding plugin and that's it. One of the fixes is doing a crazy lookup and running a generator, even though we know it is a lookup value so we should be done with all the work anyways. --- src/util/pluginUtils.js | 43 +++-- tests/match-utilities.test.js | 339 ++++++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+), 19 deletions(-) diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index 5049b088e38e..7f8c4ab74f7b 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -133,18 +133,14 @@ export function parseColorFormat(value) { return value } -export function asColor( - _, - options = {}, - { tailwindConfig = {}, utilityModifier, rawModifier } = {} -) { - if (options.values?.[rawModifier] !== undefined) { - return parseColorFormat(options.values?.[rawModifier]) +export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) { + if (options.values?.[modifier] !== undefined) { + return parseColorFormat(options.values?.[modifier]) } // TODO: Hoist this up to getMatchingTypes or something // We do this here because we need the alpha value (if any) - let [color, alpha] = splitUtilityModifier(rawModifier) + let [color, alpha] = splitUtilityModifier(modifier) if (alpha !== undefined) { let normalizedColor = @@ -167,7 +163,7 @@ export function asColor( return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha]) } - return asValue(rawModifier, options, { rawModifier, utilityModifier, validate: validateColor }) + return asValue(modifier, options, { validate: validateColor }) } export function asLookupValue(modifier, options = {}) { @@ -175,8 +171,8 @@ export function asLookupValue(modifier, options = {}) { } function guess(validate) { - return (modifier, options, extras) => { - return asValue(modifier, options, { ...extras, validate }) + return (modifier, options) => { + return asValue(modifier, options, { validate }) } } @@ -208,6 +204,22 @@ function splitAtFirst(input, delim) { } export function coerceValue(types, modifier, options, tailwindConfig) { + if (options.values && modifier in options.values) { + for (let { type } of types ?? []) { + let result = typeMap[type](modifier, options, { + tailwindConfig, + }) + + if (result === undefined) { + continue + } + + return [result, type, null] + } + + return [options.values[modifier], 'lookup', null] + } + if (isArbitraryValue(modifier)) { let arbitraryValue = modifier.slice(1, -1) let [explicitType, value] = splitAtFirst(arbitraryValue, ':') @@ -280,17 +292,10 @@ export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) { utilityModifier = utilityModifier.slice(1, -1) } } - - let result = asValue(rawModifier, options, { rawModifier, utilityModifier, tailwindConfig }) - if (result !== undefined) { - yield [result, 'any', null] - } } - for (const { type } of types ?? []) { + for (let { type } of types ?? []) { let result = typeMap[type](modifier, options, { - rawModifier, - utilityModifier, tailwindConfig, }) diff --git a/tests/match-utilities.test.js b/tests/match-utilities.test.js index 587a46866858..0e76373ef8eb 100644 --- a/tests/match-utilities.test.js +++ b/tests/match-utilities.test.js @@ -165,3 +165,342 @@ test('match utilities can omit utilities by returning null', async () => { } `) }) + +test('matching utilities with an arbitrary value and configured modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + modifiers: { + bar: 'configured_bar', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/bar { + value: foo; + modifier: configured_bar; + } + `) + }) +}) + +test('matching utilities with an configured value and an arbitrary modifier (raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + foo: 'configured_foo', + }, + modifiers: 'any', // Raw `[value]` + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/\[bar\] { + value: configured_foo; + modifier: [bar]; + } + `) + }) +}) + +test('matching utilities with an configured value and an arbitrary modifier (non-raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + foo: 'configured_foo', + }, + modifiers: {}, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/\[bar\] { + value: configured_foo; + modifier: bar; + } + `) + }) +}) + +test('matching utilities with an configured value and a configured modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + foo: 'configured_foo', + }, + modifiers: { + bar: 'configured_bar', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/bar { + value: configured_foo; + modifier: configured_bar; + } + `) + }) +}) + +test('matching utilities with an arbitrary value and an arbitrary modifier (raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + modifiers: 'any', + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: foo; + modifier: [bar]; + } + `) + }) +}) + +test('matching utilities with an arbitrary value and an arbitrary modifier (non-raw)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + modifiers: {}, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: foo; + modifier: bar; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/[bar]': 'hello', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and modifier (with modifiers = any)', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/[bar]': 'hello', + }, + modifiers: 'any', + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and modifier (with modifiers = {})', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/[bar]': 'hello', + }, + modifiers: {}, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/\[bar\] { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like an arbitrary value and a configured modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + '[foo]/bar': 'hello', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-\[foo\]\/bar { + value: hello; + } + `) + }) +}) + +test('matching utilities with a lookup value that looks like a configured value and an arbitrary modifier', () => { + let config = { + content: [{ raw: html`
` }], + theme: {}, + plugins: [ + function ({ matchUtilities }) { + matchUtilities( + { + test: (value, { modifier }) => ({ value, modifier }), + }, + { + values: { + 'foo/[bar]': 'hello', + }, + } + ) + }, + ], + corePlugins: [], + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchCss(css` + .test-foo\/\[bar\] { + value: hello; + } + `) + }) +}) From 0108e44568c1f0ac4f0fe3f8cc0810e7c585f2ac Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Thu, 17 Nov 2022 17:50:01 +0100 Subject: [PATCH 2/2] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b1981c3223c..ab1b307cac32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Cleanup unused `variantOrder` ([#9829](https://github.com/tailwindlabs/tailwindcss/pull/9829)) +- Fix `foo-[abc]/[def]` not being handled correctl ([#9866](https://github.com/tailwindlabs/tailwindcss/pull/9866)) ### Added