`,
`test`,
]
let defaultExtractor
beforeEach(() => {
let context = { tailwindConfig: { separator: ':' } }
defaultExtractor = createDefaultExtractor(context)
})
test('The default extractor works as expected', async () => {
const extractions = defaultExtractor([jsExamples, jsxExamples, htmlExamples].join('\n').trim())
for (const str of includes) {
expect(extractions).toContain(str)
}
for (const str of excludes) {
expect(extractions).not.toContain(str)
}
})
// Scenarios:
// - In double quoted class attribute
// - In single quoted class attribute
// - Single-quoted as a variable
// - Double-quoted as a variable
// - Single-quoted as first array item
// - Double-quoted as first array item
// - Single-quoted as middle array item
// - Double-quoted as middle array item
// - Single-quoted as last array item
// - Double-quoted as last array item
// - Bare as an object key (with trailing `:`)
// - Quoted as an object key (with trailing `:`)
// - Within a template literal
// - Within a template literal directly before interpolation
// - Within a template literal directly after interpolation
// - JS: ${...}
// - PHP: {$...}
// - Ruby: #{...}
// - Within a string of HTML wrapped in escaped quotes
test('basic utility classes', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('text-center')
expect(extractions).toContain('font-bold')
expect(extractions).toContain('px-4')
expect(extractions).toContain('pointer-events-none')
})
test('modifiers with basic utilities', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('hover:text-center')
expect(extractions).toContain('hover:focus:font-bold')
})
test('utilities with dot characters', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('px-1.5')
expect(extractions).toContain('active:px-2.5')
expect(extractions).toContain('hover:focus:px-3.5')
})
test('basic utilities with color opacity modifier', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('text-red-500/25')
expect(extractions).toContain('hover:text-red-500/50')
expect(extractions).toContain('hover:active:text-red-500/75')
})
test('basic arbitrary values', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('px-[25px]')
expect(extractions).toContain('hover:px-[40rem]')
expect(extractions).toContain('hover:focus:px-[23vh]')
})
test('arbitrary values with color opacity modifier', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('text-[#bada55]/25')
expect(extractions).toContain('hover:text-[#bada55]/50')
expect(extractions).toContain('hover:active:text-[#bada55]/75')
})
test('arbitrary values with spaces', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('grid-cols-[1fr_200px_3fr]')
expect(extractions).toContain('md:grid-cols-[2fr_100px_1fr]')
expect(extractions).toContain('open:lg:grid-cols-[3fr_300px_1fr]')
})
test('arbitrary values with css variables', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('fill-[var(--my-color)]')
expect(extractions).toContain('hover:fill-[var(--my-color-2)]')
expect(extractions).toContain('hover:focus:fill-[var(--my-color-3)]')
})
test('arbitrary values with type hints', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('text-[color:var(--my-color)]')
expect(extractions).toContain('hover:text-[color:var(--my-color-2)]')
expect(extractions).toContain('hover:focus:text-[color:var(--my-color-3)]')
})
test('arbitrary values with single quotes', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`content-['hello_world']`)
expect(extractions).toContain(`hover:content-['hello_world_2']`)
expect(extractions).toContain(`hover:focus:content-['hello_world_3']`)
})
test('arbitrary values with double quotes', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`content-["hello_world"]`)
expect(extractions).toContain(`hover:content-["hello_world_2"]`)
expect(extractions).toContain(`hover:focus:content-["hello_world_3"]`)
})
test('arbitrary values with some single quoted values', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`font-['Open_Sans',_system-ui,_sans-serif]`)
expect(extractions).toContain(`hover:font-['Proxima_Nova',_system-ui,_sans-serif]`)
expect(extractions).toContain(`hover:focus:font-['Inter_var',_system-ui,_sans-serif]`)
})
test('arbitrary values with some double quoted values', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`font-["Open_Sans",_system-ui,_sans-serif]`)
expect(extractions).toContain(`hover:font-["Proxima_Nova",_system-ui,_sans-serif]`)
expect(extractions).toContain(`hover:focus:font-["Inter_var",_system-ui,_sans-serif]`)
})
test('arbitrary values with escaped underscores', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`content-['hello\\_world']`)
expect(extractions).toContain(`hover:content-['hello\\_world\\_2']`)
expect(extractions).toContain(`hover:focus:content-['hello\\_world\\_3']`)
})
test('basic utilities with arbitrary color opacity modifier', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('text-red-500/[.25]')
expect(extractions).toContain('hover:text-red-500/[.5]')
expect(extractions).toContain('hover:active:text-red-500/[.75]')
})
test('arbitrary values with arbitrary color opacity modifier', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain('text-[#bada55]/[.25]')
expect(extractions).toContain('hover:text-[#bada55]/[.5]')
expect(extractions).toContain('hover:active:text-[#bada55]/[.75]')
})
test('arbitrary values with angle brackets', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`content-[>]`)
expect(extractions).toContain(`hover:content-[<]`)
expect(extractions).toContain(`hover:focus:content-[>]`)
})
test('arbitrary values with angle brackets in single quotes', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`content-['>']`)
expect(extractions).toContain(`hover:content-['<']`)
expect(extractions).toContain(`hover:focus:content-['>']`)
})
test('arbitrary values with angle brackets in double quotes', async () => {
const extractions = defaultExtractor(`
"] hover:content-["<"] hover:focus:content-[">"]">
`)
expect(extractions).toContain(`content-[">"]`)
expect(extractions).toContain(`hover:content-["<"]`)
expect(extractions).toContain(`hover:focus:content-[">"]`)
})
test('arbitrary values with theme lookup using quotes', () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`[--y:theme('colors.blue.500')]`)
expect(extractions).toContain(`[color:var(--y)]`)
})
test('special characters', async () => {
const extractions = defaultExtractor(`
`)
expect(extractions).toContain(`
:font-bold`)
})
test('with single quotes array within template literal', async () => {
const extractions = defaultExtractor(``)
expect(extractions).toContain('pr-1.5')
})
test('with double quotes array within template literal', async () => {
const extractions = defaultExtractor(``)
expect(extractions).toContain('pr-1.5')
})
test('with single quotes array within function', async () => {
const extractions = defaultExtractor(`document.body.classList.add(['pl-1.5'].join(" "));`)
expect(extractions).toContain('pl-1.5')
})
test('with double quotes array within function', async () => {
const extractions = defaultExtractor(`document.body.classList.add(["pl-1.5"].join(" "));`)
expect(extractions).toContain('pl-1.5')
})
test('with angle brackets', async () => {
const extractions = defaultExtractor(
`test
`
)
expect(extractions).toContain('bg-blue-200')
expect(extractions).toContain('shadow-xl')
expect(extractions).not.toContain('>shadow-xl')
expect(extractions).not.toContain('shadow-xl<')
})
test('markdown code fences', async () => {
const extractions = defaultExtractor('')
expect(extractions).toContain('font-bold')
expect(extractions).toContain('font-normal')
expect(extractions).not.toContain('.font-bold')
expect(extractions).not.toContain('.font-normal')
})
test('classes in slim templates', async () => {
const extractions = defaultExtractor(`
p.bg-red-500.text-sm
'This is a paragraph
small.italic.text-gray-500
'(Look mom, no closing tag!)
`)
expect(extractions).toContain('bg-red-500')
expect(extractions).toContain('text-sm')
expect(extractions).toContain('italic')
expect(extractions).toContain('text-gray-500')
})
test('multi-word + arbitrary values + quotes', async () => {
const extractions = defaultExtractor(`
grid-cols-['repeat(2)']
`)
expect(extractions).toContain(`grid-cols-['repeat(2)']`)
})
test('a lot of data', () => {
let extractions = defaultExtractor('underline '.repeat(2 ** 17))
expect(extractions).toContain(`underline`)
})