From 0f750266b875b2bf0836133b4bcadc66ab1cb45b Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Thu, 5 Jan 2023 15:37:07 +0000 Subject: [PATCH 1/4] Add modifiers to `conext.getClassList` result --- src/lib/setupContextUtils.js | 13 ++++- tests/getClassList.test.js | 101 ++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 0912ffab7467..55e37c131dbe 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -951,15 +951,24 @@ function registerPlugins(plugins, context) { let [utilName, options] = util let negativeClasses = [] + let modifiers = Object.keys(options?.modifiers ?? {}) + + if (options?.types?.some(({ type }) => type === 'color')) { + modifiers.push(...Object.keys(context.tailwindConfig.theme.opacity ?? {})) + } + for (let [key, value] of Object.entries(options?.values ?? {})) { // Ignore undefined and null values if (value == null) { continue } - output.push(formatClass(utilName, key)) + let cls = formatClass(utilName, key) + output.push(modifiers.length > 0 ? [cls, { modifiers }] : cls) + if (options?.supportsNegativeValues && negateValue(value)) { - negativeClasses.push(formatClass(utilName, `-${key}`)) + let cls = formatClass(utilName, `-${key}`) + negativeClasses.push(modifiers.length > 0 ? [cls, { modifiers }] : cls) } } diff --git a/tests/getClassList.test.js b/tests/getClassList.test.js index 8fd2df6f191c..f686e3313e48 100644 --- a/tests/getClassList.test.js +++ b/tests/getClassList.test.js @@ -1,4 +1,5 @@ import resolveConfig from '../src/public/resolve-config' +import plugin from '../src/public/create-plugin' import { createContext } from '../src/lib/setupContextUtils' it('should generate every possible class, without variants', () => { @@ -68,6 +69,82 @@ it('should not generate utilities with opacity by default', () => { expect(classes).not.toContain('bg-red-500/50') }) +it('should generate utilities with modifier data', () => { + let config = {} + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() + + expect(classes).toContainEqual([ + 'bg-red-500', + { + modifiers: [ + '0', + '5', + '10', + '20', + '25', + '30', + '40', + '50', + '60', + '70', + '75', + '80', + '90', + '95', + '100', + ], + }, + ]) + expect(classes).toContainEqual([ + 'text-2xl', + { + modifiers: [ + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + 'none', + 'tight', + 'snug', + 'normal', + 'relaxed', + 'loose', + ], + }, + ]) +}) + +it('should generate plugin-defined utilities with modifier data', () => { + let config = { + plugins: [ + plugin(function ({ matchUtilities }) { + matchUtilities( + { + foo: (value, { modifier }) => { + return { color: `rgb(${value} / ${modifier ?? 1})` } + }, + }, + { values: { red: 'red' }, modifiers: { bar: '0' } } + ) + }), + ], + } + let context = createContext(resolveConfig(config)) + let classes = context.getClassList() + + expect(classes).toContainEqual([ + 'foo-red', + { + modifiers: ['bar'], + }, + ]) +}) + it('should not generate utilities with opacity even if safe-listed', () => { let config = { safelist: [ @@ -114,6 +191,26 @@ it('should not generate utilities that are set to undefined or null to so that t expect(classes).not.toContain('bg-blue-100') // Blue.100 is `null` expect(classes).not.toContain('bg-blue-200') // Blue.200 is `undefined` - expect(classes).toContain('bg-blue-50') - expect(classes).toContain('bg-blue-300') + expect(classes).toContainEqual([ + 'bg-blue-50', + { + modifiers: [ + '0', + '5', + '10', + '20', + '25', + '30', + '40', + '50', + '60', + '70', + '75', + '80', + '90', + '95', + '100', + ], + }, + ]) }) From 82814d95a37cf51d17343113f10e3057a45cdacd Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Thu, 5 Jan 2023 15:43:28 +0000 Subject: [PATCH 2/4] Tweak test --- tests/getClassList.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/getClassList.test.js b/tests/getClassList.test.js index f686e3313e48..34410e7a192e 100644 --- a/tests/getClassList.test.js +++ b/tests/getClassList.test.js @@ -129,7 +129,7 @@ it('should generate plugin-defined utilities with modifier data', () => { return { color: `rgb(${value} / ${modifier ?? 1})` } }, }, - { values: { red: 'red' }, modifiers: { bar: '0' } } + { values: { red: '255 0 0' }, modifiers: { bar: '0' } } ) }), ], From 8ce42bc809e2e155e920dc3dd7120881355099ce Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Tue, 10 Jan 2023 16:29:38 +0000 Subject: [PATCH 3/4] Add `includeMetadata` flag and update tests --- src/lib/setupContextUtils.js | 19 +++++++++------- tests/getClassList.test.js | 44 ++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 55e37c131dbe..e6967c300083 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -943,32 +943,35 @@ function registerPlugins(plugins, context) { // Generate a list of strings for autocompletion purposes, e.g. // ['uppercase', 'lowercase', ...] - context.getClassList = function getClassList() { + context.getClassList = function getClassList(options = {}) { let output = [] for (let util of classList) { if (Array.isArray(util)) { - let [utilName, options] = util + let [utilName, utilOptions] = util let negativeClasses = [] - let modifiers = Object.keys(options?.modifiers ?? {}) + let modifiers = Object.keys(utilOptions?.modifiers ?? {}) - if (options?.types?.some(({ type }) => type === 'color')) { + if (utilOptions?.types?.some(({ type }) => type === 'color')) { modifiers.push(...Object.keys(context.tailwindConfig.theme.opacity ?? {})) } - for (let [key, value] of Object.entries(options?.values ?? {})) { + let metadata = { modifiers } + let includeMetadata = options.includeMetadata && modifiers.length > 0 + + for (let [key, value] of Object.entries(utilOptions?.values ?? {})) { // Ignore undefined and null values if (value == null) { continue } let cls = formatClass(utilName, key) - output.push(modifiers.length > 0 ? [cls, { modifiers }] : cls) + output.push(includeMetadata ? [cls, metadata] : cls) - if (options?.supportsNegativeValues && negateValue(value)) { + if (utilOptions?.supportsNegativeValues && negateValue(value)) { let cls = formatClass(utilName, `-${key}`) - negativeClasses.push(modifiers.length > 0 ? [cls, { modifiers }] : cls) + negativeClasses.push(includeMetadata ? [cls, metadata] : cls) } } diff --git a/tests/getClassList.test.js b/tests/getClassList.test.js index 34410e7a192e..e65f99cc5e53 100644 --- a/tests/getClassList.test.js +++ b/tests/getClassList.test.js @@ -69,11 +69,25 @@ it('should not generate utilities with opacity by default', () => { expect(classes).not.toContain('bg-red-500/50') }) -it('should generate utilities with modifier data', () => { +it('should not include metadata by default', () => { let config = {} let context = createContext(resolveConfig(config)) let classes = context.getClassList() + expect(classes.every((cls) => typeof cls === 'string')).toEqual(true) + + expect(classes).toContain('bg-red-500') + expect(classes).toContain('text-2xl') +}) + +it('should generate utilities with modifier data when requested', () => { + let config = {} + let context = createContext(resolveConfig(config)) + let classes = context.getClassList({ includeMetadata: true }) + + expect(classes).not.toContain('bg-red-500') + expect(classes).not.toContain('text-2xl') + expect(classes).toContainEqual([ 'bg-red-500', { @@ -119,7 +133,7 @@ it('should generate utilities with modifier data', () => { ]) }) -it('should generate plugin-defined utilities with modifier data', () => { +it('should generate plugin-defined utilities with modifier data when requested', () => { let config = { plugins: [ plugin(function ({ matchUtilities }) { @@ -135,7 +149,7 @@ it('should generate plugin-defined utilities with modifier data', () => { ], } let context = createContext(resolveConfig(config)) - let classes = context.getClassList() + let classes = context.getClassList({ includeMetadata: true }) expect(classes).toContainEqual([ 'foo-red', @@ -191,26 +205,6 @@ it('should not generate utilities that are set to undefined or null to so that t expect(classes).not.toContain('bg-blue-100') // Blue.100 is `null` expect(classes).not.toContain('bg-blue-200') // Blue.200 is `undefined` - expect(classes).toContainEqual([ - 'bg-blue-50', - { - modifiers: [ - '0', - '5', - '10', - '20', - '25', - '30', - '40', - '50', - '60', - '70', - '75', - '80', - '90', - '95', - '100', - ], - }, - ]) + expect(classes).toContain('bg-blue-50') + expect(classes).toContain('bg-blue-300') }) From 604d54caf596a9b4e41f4375db61a4f210a0a283 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Wed, 25 Jan 2023 18:25:39 +0000 Subject: [PATCH 4/4] update test --- tests/getClassList.test.js | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/getClassList.test.js b/tests/getClassList.test.js index ce9b89244f35..671a767ecfe5 100644 --- a/tests/getClassList.test.js +++ b/tests/getClassList.test.js @@ -141,11 +141,26 @@ crosscheck(() => { plugin(function ({ matchUtilities }) { matchUtilities( { - foo: (value, { modifier }) => { - return { color: `rgb(${value} / ${modifier ?? 1})` } + foo: (value) => { + return { margin: value } }, }, - { values: { red: '255 0 0' }, modifiers: { bar: '0' } } + { + values: { xl: '32px' }, + modifiers: { bar: 'something' }, + } + ) + matchUtilities( + { + 'foo-negative': (value) => { + return { margin: value } + }, + }, + { + values: { xl: '32px' }, + modifiers: { bar: 'something' }, + supportsNegativeValues: true, + } ) }), ], @@ -153,12 +168,12 @@ crosscheck(() => { let context = createContext(resolveConfig(config)) let classes = context.getClassList({ includeMetadata: true }) - expect(classes).toContainEqual([ - 'foo-red', - { - modifiers: ['bar'], - }, - ]) + expect(classes).toContainEqual(['foo-xl', { modifiers: ['bar'] }]) + expect(classes).toContainEqual(['foo-negative-xl', { modifiers: ['bar'] }]) + expect(classes).toContainEqual(['-foo-negative-xl', { modifiers: ['bar'] }]) + expect(classes).not.toContain('foo-xl') + expect(classes).not.toContain('-foo-xl') + expect(classes).not.toContainEqual(['-foo-xl', { modifiers: ['bar'] }]) }) it('should not generate utilities with opacity even if safe-listed', () => {