diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fcaccedde7a..c149d888e886 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added 'place-items-baseline' utility ([#9507](https://github.com/tailwindlabs/tailwindcss/pull/9507)) - Added 'content-baseline' utility ([#9507](https://github.com/tailwindlabs/tailwindcss/pull/9507)) - Prepare for container queries setup ([#9526](https://github.com/tailwindlabs/tailwindcss/pull/9526)) +- Add support for modifiers to `matchUtilities` ([#9541](https://github.com/tailwindlabs/tailwindcss/pull/9541)) +- Switch to positional argument + object for modifiers ([#9541](https://github.com/tailwindlabs/tailwindcss/pull/9541)) ### Fixed diff --git a/src/corePlugins.js b/src/corePlugins.js index bd1e43c160ff..0ae64d9b0762 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -144,28 +144,27 @@ export let variantPlugins = { } let variants = { - group: ({ modifier }) => + group: (_, { modifier }) => modifier ? [`:merge(.group\\/${modifier})`, ' &'] : [`:merge(.group)`, ' &'], - peer: ({ modifier }) => + peer: (_, { modifier }) => modifier ? [`:merge(.peer\\/${modifier})`, ' ~ &'] : [`:merge(.peer)`, ' ~ &'], } for (let [name, fn] of Object.entries(variants)) { matchVariant( name, - (ctx = {}) => { - let { modifier, value = '' } = ctx - if (modifier) { + (value = '', extra) => { + if (extra.modifier) { log.warn(`modifier-${name}-experimental`, [ `The ${name} variant modifier feature in Tailwind CSS is currently in preview.`, 'Preview features are not covered by semver, and may be improved in breaking ways at any time.', ]) } - let result = normalize(typeof value === 'function' ? value(ctx) : value) + let result = normalize(typeof value === 'function' ? value(extra) : value) if (!result.includes('&')) result = '&' + result - let [a, b] = fn({ modifier }) + let [a, b] = fn('', extra) return result.replace(/&(\S+)?/g, (_, pseudo = '') => a + pseudo + b) }, { values: Object.fromEntries(pseudoVariants) } @@ -232,7 +231,7 @@ export let variantPlugins = { supportsVariants: ({ matchVariant, theme }) => { matchVariant( 'supports', - ({ value = '' }) => { + (value = '') => { let check = normalize(value) let isRaw = /^\w*\s*\(/.test(check) diff --git a/src/featureFlags.js b/src/featureFlags.js index 2bfee94d97ab..13c424184768 100644 --- a/src/featureFlags.js +++ b/src/featureFlags.js @@ -14,6 +14,7 @@ let featureFlags = { ], experimental: [ 'optimizeUniversalDefaults', + 'generalizedModifiers', // 'variantGrouping', ], } diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index 202004903c21..961b6fa5e5af 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -3,7 +3,7 @@ import selectorParser from 'postcss-selector-parser' import parseObjectStyles from '../util/parseObjectStyles' import isPlainObject from '../util/isPlainObject' import prefixSelector from '../util/prefixSelector' -import { updateAllClasses, typeMap } from '../util/pluginUtils' +import { updateAllClasses, getMatchingTypes } from '../util/pluginUtils' import log from '../util/log' import * as sharedState from './sharedState' import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector' @@ -34,13 +34,24 @@ function* candidatePermutations(candidate) { while (lastIndex >= 0) { let dashIdx + let wasSlash = false if (lastIndex === Infinity && candidate.endsWith(']')) { let bracketIdx = candidate.indexOf('[') // If character before `[` isn't a dash or a slash, this isn't a dynamic class // eg. string[] - dashIdx = ['-', '/'].includes(candidate[bracketIdx - 1]) ? bracketIdx - 1 : -1 + if (candidate[bracketIdx - 1] === '-') { + dashIdx = bracketIdx - 1 + } else if (candidate[bracketIdx - 1] === '/') { + dashIdx = bracketIdx - 1 + wasSlash = true + } else { + dashIdx = -1 + } + } else if (lastIndex === Infinity && candidate.includes('/')) { + dashIdx = candidate.lastIndexOf('/') + wasSlash = true } else { dashIdx = candidate.lastIndexOf('-', lastIndex) } @@ -50,11 +61,16 @@ function* candidatePermutations(candidate) { } let prefix = candidate.slice(0, dashIdx) - let modifier = candidate.slice(dashIdx + 1) - - yield [prefix, modifier] + let modifier = candidate.slice(wasSlash ? dashIdx : dashIdx + 1) lastIndex = dashIdx - 1 + + // TODO: This feels a bit hacky + if (prefix === '' || modifier === '/') { + continue + } + + yield [prefix, modifier] } } @@ -137,6 +153,10 @@ function applyVariant(variant, matches, context) { if (match) { variant = match[1] args.modifier = match[2] + + if (!flagEnabled(context.tailwindConfig, 'generalizedModifiers')) { + return [] + } } } @@ -552,16 +572,14 @@ function* resolveMatches(candidate, context, original = candidate) { } if (matchesPerPlugin.length > 0) { - let matchingTypes = (sort.options?.types ?? []) - .map(({ type }) => type) - // Only track the types for this plugin that resulted in some result - .filter((type) => { - return Boolean( - typeMap[type](modifier, sort.options, { - tailwindConfig: context.tailwindConfig, - }) - ) - }) + let matchingTypes = Array.from( + getMatchingTypes( + sort.options?.types ?? [], + modifier, + sort.options ?? {}, + context.tailwindConfig + ) + ).map(([_, type]) => type) if (matchingTypes.length > 0) { typesByMatches.set(matchesPerPlugin, matchingTypes) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index fa0969ffb1a4..2f0ca84084b5 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -21,6 +21,7 @@ import isValidArbitraryValue from '../util/isValidArbitraryValue' import { generateRules } from './generateRules' import { hasContentChanged } from './cacheInvalidation.js' import { Offsets } from './offsets.js' +import { flagEnabled } from '../featureFlags.js' let MATCH_VARIANT = Symbol() @@ -358,6 +359,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs let defaultOptions = { respectPrefix: true, respectImportant: true, + modifiers: false, } options = normalizeOptionTypes({ ...defaultOptions, ...options }) @@ -371,7 +373,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs classList.add([prefixedIdentifier, options]) function wrapped(modifier, { isOnlyPlugin }) { - let [value, coercedType] = coerceValue(options.types, modifier, options, tailwindConfig) + let [value, coercedType, utilityModifier] = coerceValue( + options.types, + modifier, + options, + tailwindConfig + ) if (value === undefined) { return [] @@ -395,8 +402,22 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs return [] } + let extras = { + get modifier() { + if (!options.modifiers) { + log.warn(`modifier-used-without-options-for-${identifier}`, [ + 'Your plugin must set `modifiers: true` in its options to support modifiers.', + ]) + } + + return utilityModifier + }, + } + + let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') + let ruleSets = [] - .concat(rule(value)) + .concat(modifiersEnabled ? rule(value, extras) : rule(value)) .filter(Boolean) .map((declaration) => ({ [nameClass(identifier, modifier)]: declaration, @@ -418,6 +439,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs let defaultOptions = { respectPrefix: true, respectImportant: false, + modifiers: false, } options = normalizeOptionTypes({ ...defaultOptions, ...options }) @@ -431,7 +453,12 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs classList.add([prefixedIdentifier, options]) function wrapped(modifier, { isOnlyPlugin }) { - let [value, coercedType] = coerceValue(options.types, modifier, options, tailwindConfig) + let [value, coercedType, utilityModifier] = coerceValue( + options.types, + modifier, + options, + tailwindConfig + ) if (value === undefined) { return [] @@ -455,8 +482,22 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs return [] } + let extras = { + get modifier() { + if (!options.modifiers) { + log.warn(`modifier-used-without-options-for-${identifier}`, [ + 'Your plugin must set `modifiers: true` in its options to support modifiers.', + ]) + } + + return utilityModifier + }, + } + + let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') + let ruleSets = [] - .concat(rule(value)) + .concat(modifiersEnabled ? rule(value, extras) : rule(value)) .filter(Boolean) .map((declaration) => ({ [nameClass(identifier, modifier)]: declaration, @@ -522,21 +563,37 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs let id = ++variantIdentifier // A unique identifier that "groups" these variables together. let isSpecial = variant === '@' + let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') + for (let [key, value] of Object.entries(options?.values ?? {})) { api.addVariant( isSpecial ? `${variant}${key}` : `${variant}-${key}`, - Object.assign(({ args, container }) => variantFn({ ...args, container, value }), { - [MATCH_VARIANT]: true, - }), + Object.assign( + ({ args, container }) => + variantFn( + value, + modifiersEnabled ? { modifier: args.modifier, container } : { container } + ), + { + [MATCH_VARIANT]: true, + } + ), { ...options, value, id } ) } api.addVariant( variant, - Object.assign(({ args, container }) => variantFn({ ...args, container }), { - [MATCH_VARIANT]: true, - }), + Object.assign( + ({ args, container }) => + variantFn( + args.value, + modifiersEnabled ? { modifier: args.modifier, container } : { container } + ), + { + [MATCH_VARIANT]: true, + } + ), { ...options, id } ) }, diff --git a/src/util/nameClass.js b/src/util/nameClass.js index ae737012901b..cb37dba20d09 100644 --- a/src/util/nameClass.js +++ b/src/util/nameClass.js @@ -22,5 +22,9 @@ export function formatClass(classPrefix, key) { return `-${classPrefix}${key}` } + if (key.startsWith('/')) { + return `${classPrefix}${key}` + } + return `${classPrefix}-${key}` } diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index c56f6a1dd412..d5ad3fa3142b 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -19,6 +19,7 @@ import { } from './dataTypes' import negateValue from './negateValue' import { backgroundSize } from './validateFormalSyntax' +import { flagEnabled } from '../featureFlags.js' export function updateAllClasses(selectors, updateClass) { let parser = selectorParser((selectors) => { @@ -86,11 +87,20 @@ function isArbitraryValue(input) { return input.startsWith('[') && input.endsWith(']') } -function splitAlpha(modifier) { +function splitUtilityModifier(modifier) { let slashIdx = modifier.lastIndexOf('/') if (slashIdx === -1 || slashIdx === modifier.length - 1) { - return [modifier] + return [modifier, undefined] + } + + let arbitrary = isArbitraryValue(modifier) + + // The modifier could be of the form `[foo]/[bar]` + // We want to handle this case properly + // without affecting `[foo/bar]` + if (arbitrary && !modifier.includes(']/[')) { + return [modifier, undefined] } return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)] @@ -106,12 +116,18 @@ export function parseColorFormat(value) { return value } -export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) { - if (options.values?.[modifier] !== undefined) { - return parseColorFormat(options.values?.[modifier]) +export function asColor( + _, + options = {}, + { tailwindConfig = {}, utilityModifier, rawModifier } = {} +) { + if (options.values?.[rawModifier] !== undefined) { + return parseColorFormat(options.values?.[rawModifier]) } - let [color, alpha] = splitAlpha(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) if (alpha !== undefined) { let normalizedColor = @@ -134,7 +150,7 @@ export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) { return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha]) } - return asValue(modifier, options, { validate: validateColor }) + return asValue(rawModifier, options, { rawModifier, utilityModifier, validate: validateColor }) } export function asLookupValue(modifier, options = {}) { @@ -142,8 +158,8 @@ export function asLookupValue(modifier, options = {}) { } function guess(validate) { - return (modifier, options) => { - return asValue(modifier, options, { validate }) + return (modifier, options, extras) => { + return asValue(modifier, options, { ...extras, validate }) } } @@ -192,15 +208,73 @@ export function coerceValue(types, modifier, options, tailwindConfig) { } if (value.length > 0 && supportedTypes.includes(explicitType)) { - return [asValue(`[${value}]`, options), explicitType] + return [asValue(`[${value}]`, options), explicitType, null] } } + let matches = getMatchingTypes(types, modifier, options, tailwindConfig) + // Find first matching type - for (let { type } of types) { - let result = typeMap[type](modifier, options, { tailwindConfig }) - if (result !== undefined) return [result, type] + for (let match of matches) { + return match } return [] } + +/** + * + * @param {{type: string}[]} types + * @param {string} rawModifier + * @param {any} options + * @param {any} tailwindConfig + * @returns {Iterator<[value: string, type: string, modifier: string | null]>} + */ +export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) { + let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers') + + let canUseUtilityModifier = + modifiersEnabled && + options.modifiers != null && + (options.modifiers === 'any' || typeof options.modifiers === 'object') + + let [modifier, utilityModifier] = canUseUtilityModifier + ? splitUtilityModifier(rawModifier) + : [rawModifier, undefined] + + if (utilityModifier !== undefined && modifier === '') { + modifier = 'DEFAULT' + } + + // Check the full value first + // TODO: Move to asValue… somehow + if (utilityModifier !== undefined) { + if (typeof options.modifiers === 'object') { + let configValue = options.modifiers?.[utilityModifier] ?? null + if (configValue !== null) { + utilityModifier = configValue + } else if (isArbitraryValue(utilityModifier)) { + 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 ?? []) { + let result = typeMap[type](modifier, options, { + rawModifier, + utilityModifier, + tailwindConfig, + }) + + if (result === undefined) { + continue + } + + yield [result, type, utilityModifier ?? null] + } +} diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index d259971fe57a..39484315d066 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -709,6 +709,9 @@ it('should support supports', () => { it('should be possible to use modifiers and arbitrary groups', () => { let config = { + experimental: { + generalizedModifiers: true, + }, content: [ { raw: html` @@ -810,6 +813,9 @@ it('should be possible to use modifiers and arbitrary groups', () => { it('should be possible to use modifiers and arbitrary peers', () => { let config = { + experimental: { + generalizedModifiers: true, + }, content: [ { raw: html` diff --git a/tests/match-utilities.test.js b/tests/match-utilities.test.js new file mode 100644 index 000000000000..bae11e5a450a --- /dev/null +++ b/tests/match-utilities.test.js @@ -0,0 +1,177 @@ +import { run, html, css } from './util/run' + +test('match utilities with modifiers', async () => { + let config = { + experimental: { + generalizedModifiers: true, + }, + + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + + plugins: [ + ({ matchUtilities }) => { + matchUtilities( + { + test: (value, { modifier }) => ({ + color: `${value}_${modifier}`, + }), + }, + { + values: { + DEFAULT: 'default', + bar: 'bar', + '1': 'one', + '2': 'two', + '1/foo': 'onefoo', + }, + modifiers: 'any', + } + ) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .test { + color: default_null; + } + .test\/foo { + color: default_foo; + } + .test-1\/foo { + color: onefoo_null; + } + .test-2\/foo { + color: two_foo; + } + .test\/\[foo\] { + color: default_[foo]; + } + .test-1\/\[foo\] { + color: one_[foo]; + } + `) +}) + +test('match utilities with modifiers in the config', async () => { + let config = { + experimental: { + generalizedModifiers: true, + }, + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + + plugins: [ + ({ matchUtilities }) => { + matchUtilities( + { + test: (value, { modifier }) => ({ + color: `${value}_${modifier}`, + }), + }, + { + values: { + DEFAULT: 'default', + bar: 'bar', + '1': 'one', + }, + modifiers: { + foo: 'mewtwo', + }, + } + ) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .test { + color: default_null; + } + .test\/foo { + color: default_mewtwo; + } + .test-1\/foo { + color: one_mewtwo; + } + .test\/\[bar\] { + color: default_bar; + } + .test-1\/\[bar\] { + color: one_bar; + } + `) +}) + +test('match utilities can omit utilities by returning null', async () => { + let config = { + experimental: { + generalizedModifiers: true, + }, + content: [ + { + raw: html`
`, + }, + ], + corePlugins: { preflight: false }, + + plugins: [ + ({ matchUtilities }) => { + matchUtilities( + { + test: (value, { modifier }) => + modifier === 'bad' + ? null + : { + color: `${value}_${modifier}`, + }, + }, + { + values: { + DEFAULT: 'default', + bar: 'bar', + '1': 'one', + }, + modifiers: 'any', + } + ) + }, + ], + } + + let input = css` + @tailwind utilities; + ` + + let result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .test { + color: default_null; + } + .test\/good { + color: default_good; + } + `) +}) diff --git a/tests/match-variants.test.js b/tests/match-variants.test.js index 7b80a0639eb3..fad17193617d 100644 --- a/tests/match-variants.test.js +++ b/tests/match-variants.test.js @@ -10,7 +10,7 @@ test('partial arbitrary variants', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('potato', ({ value: flavor }) => `.potato-${flavor} &`) + matchVariant('potato', (flavor) => `.potato-${flavor} &`) }, ], } @@ -43,7 +43,7 @@ test('partial arbitrary variants with at-rules', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('potato', ({ value: flavor }) => `@media (potato: ${flavor})`) + matchVariant('potato', (flavor) => `@media (potato: ${flavor})`) }, ], } @@ -79,7 +79,7 @@ test('partial arbitrary variants with at-rules and placeholder', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('potato', ({ value: flavor }) => `@media (potato: ${flavor}) { &:potato }`) + matchVariant('potato', (flavor) => `@media (potato: ${flavor}) { &:potato }`) }, ], } @@ -115,7 +115,7 @@ test('partial arbitrary variants with default values', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('tooltip', ({ value: side }) => `&${side}`, { + matchVariant('tooltip', (side) => `&${side}`, { values: { bottom: '[data-location="bottom"]', top: '[data-location="top"]', @@ -154,7 +154,7 @@ test('matched variant values maintain the sort order they are registered in', () corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('alphabet', ({ value: side }) => `&${side}`, { + matchVariant('alphabet', (side) => `&${side}`, { values: { a: '[data-value="a"]', b: '[data-value="b"]', @@ -201,7 +201,7 @@ test('matchVariant can return an array of format strings from the function', () corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('test', ({ value: selector }) => + matchVariant('test', (selector) => selector.split(',').map((selector) => `&.${selector} > *`) ) }, @@ -243,7 +243,7 @@ it('should be possible to sort variants', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('min', ({ value }) => `@media (min-width: ${value})`, { + matchVariant('min', (value) => `@media (min-width: ${value})`, { sort(a, z) { return parseInt(a.value) - parseInt(z.value) }, @@ -287,7 +287,7 @@ it('should be possible to compare arbitrary variants and hardcoded variants', () corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('min', ({ value }) => `@media (min-width: ${value})`, { + matchVariant('min', (value) => `@media (min-width: ${value})`, { values: { example: '600px', }, @@ -347,13 +347,13 @@ it('should be possible to sort stacked arbitrary variants correctly', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('min', ({ value }) => `@media (min-width: ${value})`, { + matchVariant('min', (value) => `@media (min-width: ${value})`, { sort(a, z) { return parseInt(a.value) - parseInt(z.value) }, }) - matchVariant('max', ({ value }) => `@media (max-width: ${value})`, { + matchVariant('max', (value) => `@media (max-width: ${value})`, { sort(a, z) { return parseInt(z.value) - parseInt(a.value) }, @@ -412,13 +412,13 @@ it('should maintain sort from other variants, if sort functions of arbitrary var corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('min', ({ value }) => `@media (min-width: ${value})`, { + matchVariant('min', (value) => `@media (min-width: ${value})`, { sort(a, z) { return parseInt(a.value) - parseInt(z.value) }, }) - matchVariant('max', ({ value }) => `@media (max-width: ${value})`, { + matchVariant('max', (value) => `@media (max-width: ${value})`, { sort(a, z) { return parseInt(z.value) - parseInt(a.value) }, @@ -464,12 +464,12 @@ it('should sort arbitrary variants left to right (1)', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('min', ({ value }) => `@media (min-width: ${value})`, { + matchVariant('min', (value) => `@media (min-width: ${value})`, { sort(a, z) { return parseInt(a.value) - parseInt(z.value) }, }) - matchVariant('max', ({ value }) => `@media (max-width: ${value})`, { + matchVariant('max', (value) => `@media (max-width: ${value})`, { sort(a, z) { return parseInt(z.value) - parseInt(a.value) }, @@ -532,12 +532,12 @@ it('should sort arbitrary variants left to right (2)', () => { corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('min', ({ value }) => `@media (min-width: ${value})`, { + matchVariant('min', (value) => `@media (min-width: ${value})`, { sort(a, z) { return parseInt(a.value) - parseInt(z.value) }, }) - matchVariant('max', ({ value }) => `@media (max-width: ${value})`, { + matchVariant('max', (value) => `@media (max-width: ${value})`, { sort(a, z) { return parseInt(z.value) - parseInt(a.value) }, @@ -598,7 +598,7 @@ it('should guarantee that we are not passing values from other variants to the w corePlugins: { preflight: false }, plugins: [ ({ matchVariant }) => { - matchVariant('min', ({ value }) => `@media (min-width: ${value})`, { + matchVariant('min', (value) => `@media (min-width: ${value})`, { sort(a, z) { let lookup = ['100px', '200px'] if (lookup.indexOf(a.value) === -1 || lookup.indexOf(z.value) === -1) { @@ -607,7 +607,7 @@ it('should guarantee that we are not passing values from other variants to the w return lookup.indexOf(a.value) - lookup.indexOf(z.value) }, }) - matchVariant('max', ({ value }) => `@media (max-width: ${value})`, { + matchVariant('max', (value) => `@media (max-width: ${value})`, { sort(a, z) { let lookup = ['300px', '400px'] if (lookup.indexOf(a.value) === -1 || lookup.indexOf(z.value) === -1) { diff --git a/types/config.d.ts b/types/config.d.ts index 2e6fb1438019..0df3620611c9 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -12,7 +12,7 @@ interface RecursiveKeyValuePair { [key: string]: V | RecursiveKeyValuePair } type ResolvableTo = T | ((utils: PluginUtils) => T) -type CSSRuleObject = RecursiveKeyValuePair +type CSSRuleObject = RecursiveKeyValuePair interface PluginUtils { colors: DefaultColors @@ -263,13 +263,17 @@ export interface PluginAPI { }> ): void // for registering new dynamic utility styles - matchUtilities( - utilities: KeyValuePair CSSRuleObject>, + matchUtilities( + utilities: KeyValuePair< + string, + (value: T | string, extra: { modifier: U | string | null }) => CSSRuleObject | null + >, options?: Partial<{ respectPrefix: boolean respectImportant: boolean type: ValueType | ValueType[] values: KeyValuePair + modifiers: 'any' | KeyValuePair supportsNegativeValues: boolean }> ): void @@ -282,13 +286,17 @@ export interface PluginAPI { }> ): void // for registering new dynamic component styles - matchComponents( - components: KeyValuePair CSSRuleObject>, + matchComponents( + components: KeyValuePair< + string, + (value: T | string, extra: { modifier: U | string | null }) => CSSRuleObject | null + >, options?: Partial<{ respectPrefix: boolean respectImportant: boolean type: ValueType | ValueType[] values: KeyValuePair + modifiers: 'any' | KeyValuePair supportsNegativeValues: boolean }> ): void @@ -296,18 +304,14 @@ export interface PluginAPI { addBase(base: CSSRuleObject | CSSRuleObject[]): void // for registering custom variants addVariant(name: string, definition: string | string[] | (() => string) | (() => string)[]): void - matchVariant( - name: string, - cb: (options: { value: string; modifier: string | null }) => string | string[] - ): void - matchVariant( + matchVariant( name: string, - cb: (options: { value: string; modifier: string | null }) => string | string[], - options: { - values: Values - sort( - a: { value: keyof Values | string; modifier: string | null }, - b: { value: keyof Values | string; modifier: string | null } + cb: (value: T | string, extra: { modifier: string | null }) => string | string[], + options?: { + values?: KeyValuePair + sort?( + a: { value: T | string; modifier: string | null }, + b: { value: T | string; modifier: string | null } ): number } ): void @@ -327,7 +331,10 @@ export type PluginCreator = (api: PluginAPI) => void export type PluginsConfig = ( | PluginCreator | { handler: PluginCreator; config?: Partial } - | { (options: any): { handler: PluginCreator; config?: Partial }; __isOptionsFunction: true } + | { + (options: any): { handler: PluginCreator; config?: Partial } + __isOptionsFunction: true + } )[] // Top level config related