Fix foo-[abc]/[def] not being handled correctl (#9866)

* 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.

* update changelog
This commit is contained in:
Robin Malfait 2022-11-17 17:53:12 +01:00 • committed by GitHub
parent bc3d38b08b
commit 8b4471778b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 364 additions and 19 deletions

View file

@ -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

View file

@ -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,
})

View file

@ -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`<div class="test-[foo]/bar"></div>` }],
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`<div class="test-foo/[bar]"></div>` }],
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`<div class="test-foo/[bar]"></div>` }],
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`<div class="test-foo/bar"></div>` }],
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`<div class="test-[foo]/[bar]"></div>` }],
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`<div class="test-[foo]/[bar]"></div>` }],
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`<div class="test-[foo]/[bar]"></div>` }],
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`<div class="test-[foo]/[bar]"></div>` }],
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`<div class="test-[foo]/[bar]"></div>` }],
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`<div class="test-[foo]/bar"></div>` }],
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`<div class="test-foo/[bar]"></div>` }],
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;
}
`)
})
})