Skip to content

Commit da7396c

Browse files
committed
Sketching out more specific tests for our default extractor
1 parent 7114fa9 commit da7396c

File tree

2 files changed

+230
-1
lines changed

2 files changed

+230
-1
lines changed

src/lib/defaultExtractor.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ const PATTERNS = [
1414
/([^<>"'`\s]*\[[^<>"'`\s]*:"[^"'`\s]*"\])/.source, // `[content:"hello"]` but not `[content:'hello']`
1515
/([^<>"'`\s]*\[[^"'`\s]+\][^<>"'`\s]*)/.source, // `fill-[#bada55]`, `fill-[#bada55]/50`
1616
/([^<>"'`\s]*[^"'`\s:\\])/.source, // `px-1.5`, `uppercase` but not `uppercase:`
17+
18+
// Arbitrary properties
19+
// /([^"\s]*\[[^\s]+?\][^"\s]*)/.source,
20+
// /([^'\s]*\[[^\s]+?\][^'\s]*)/.source,
21+
// /([^`\s]*\[[^\s]+?\][^`\s]*)/.source,
1722
].join('|')
1823

1924
const BROAD_MATCH_GLOBAL_REGEXP = new RegExp(PATTERNS, 'g')
@@ -29,3 +34,13 @@ export function defaultExtractor(content) {
2934

3035
return results
3136
}
37+
38+
// Regular utilities
39+
// {{modifier}:}*{namespace}{-{suffix}}*{/{opacityModifier}}?
40+
41+
// Arbitrary values
42+
// {{modifier}:}*{namespace}-[{arbitraryValue}]{/{opacityModifier}}?
43+
// arbitraryValue: no whitespace, balanced quotes unless within quotes, balanced brackets unless within quotes
44+
45+
// Arbitrary properties
46+
// {{modifier}:}*[{validCssPropertyName}:{arbitraryValue}]

tests/default-extractor.test.js

Lines changed: 215 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const input =
2525
<div class="uppercase:"></div>
2626
<div class="hover:font-bold"></div>
2727
<div class="content-['>']"></div>
28+
<div class="[--y:theme(colors.blue.500)]">
2829
2930
<script>
3031
let classes01 = ["text-[10px]"]
@@ -43,7 +44,7 @@ const input =
4344
let classes14 = ["<div class='hover:test'>"]
4445
4546
let obj = {
46-
uppercase:true
47+
lowercase: true
4748
}
4849
</script>
4950
` + jsxExample
@@ -65,6 +66,7 @@ const includes = [
6566
`fill-[#bada55]/50`,
6667
`px-1.5`,
6768
`uppercase`,
69+
`lowercase`,
6870
`hover:font-bold`,
6971
`text-sm`,
7072
`text-[10px]`,
@@ -87,6 +89,7 @@ const includes = [
8789
`content-['>']`,
8890
`hover:test`,
8991
`overflow-scroll`,
92+
`[--y:theme(colors.blue.500)]`,
9093
]
9194

9295
const excludes = [
@@ -109,3 +112,214 @@ test('The default extractor works as expected', async () => {
109112
expect(extractions).not.toContain(str)
110113
}
111114
})
115+
116+
// Scenarios:
117+
// - In double quoted class attribute
118+
// - In single quoted class attribute
119+
// - Single-quoted as a variable
120+
// - Double-quoted as a variable
121+
// - Single-quoted as first array item
122+
// - Double-quoted as first array item
123+
// - Single-quoted as middle array item
124+
// - Double-quoted as middle array item
125+
// - Single-quoted as last array item
126+
// - Double-quoted as last array item
127+
// - Bare as an object key (with trailing `:`)
128+
// - Quoted as an object key (with trailing `:`)
129+
// - Within a template literal
130+
// - Within a template literal directly before interpolation
131+
// - Within a template literal directly after interpolation
132+
// - JS: ${...}
133+
// - PHP: {$...}
134+
// - Ruby: #{...}
135+
// - Within a string of HTML wrapped in escaped quotes
136+
137+
test('basic utility classes', async () => {
138+
const extractions = defaultExtractor(`
139+
<div class="text-center font-bold px-4 pointer-events-none"></div>
140+
`)
141+
142+
expect(extractions).toContain('text-center')
143+
expect(extractions).toContain('font-bold')
144+
expect(extractions).toContain('px-4')
145+
expect(extractions).toContain('pointer-events-none')
146+
})
147+
148+
test('modifiers with basic utilites', async () => {
149+
const extractions = defaultExtractor(`
150+
<div class="hover:text-center hover:focus:font-bold"></div>
151+
`)
152+
153+
expect(extractions).toContain('hover:text-center')
154+
expect(extractions).toContain('hover:focus:font-bold')
155+
})
156+
157+
test('utilities with dot characters', async () => {
158+
const extractions = defaultExtractor(`
159+
<div class="px-1.5 active:px-2.5 hover:focus:px-3.5"></div>
160+
`)
161+
162+
expect(extractions).toContain('px-1.5')
163+
expect(extractions).toContain('active:px-2.5')
164+
expect(extractions).toContain('hover:focus:px-3.5')
165+
})
166+
167+
test('basic utilities with color opacity modifier', async () => {
168+
const extractions = defaultExtractor(`
169+
<div class="text-red-500/25 hover:text-red-500/50 hover:active:text-red-500/75"></div>
170+
`)
171+
172+
expect(extractions).toContain('text-red-500/25')
173+
expect(extractions).toContain('hover:text-red-500/50')
174+
expect(extractions).toContain('hover:active:text-red-500/75')
175+
})
176+
177+
test('basic arbitrary values', async () => {
178+
const extractions = defaultExtractor(`
179+
<div class="px-[25px] hover:px-[40rem] hover:focus:px-[23vh]"></div>
180+
`)
181+
182+
expect(extractions).toContain('px-[25px]')
183+
expect(extractions).toContain('hover:px-[40rem]')
184+
expect(extractions).toContain('hover:focus:px-[23vh]')
185+
})
186+
187+
test('arbitrary values with color opacity modifier', async () => {
188+
const extractions = defaultExtractor(`
189+
<div class="text-[#bada55]/25 hover:text-[#bada55]/50 hover:active:text-[#bada55]/75"></div>
190+
`)
191+
192+
expect(extractions).toContain('text-[#bada55]/25')
193+
expect(extractions).toContain('hover:text-[#bada55]/50')
194+
expect(extractions).toContain('hover:active:text-[#bada55]/75')
195+
})
196+
197+
test('arbitrary values with spaces', async () => {
198+
const extractions = defaultExtractor(`
199+
<div class="grid-cols-[1fr_200px_3fr] md:grid-cols-[2fr_100px_1fr] open:lg:grid-cols-[3fr_300px_1fr]"></div>
200+
`)
201+
202+
expect(extractions).toContain('grid-cols-[1fr_200px_3fr]')
203+
expect(extractions).toContain('md:grid-cols-[2fr_100px_1fr]')
204+
expect(extractions).toContain('open:lg:grid-cols-[3fr_300px_1fr]')
205+
})
206+
207+
test('arbitrary values with css variables', async () => {
208+
const extractions = defaultExtractor(`
209+
<div class="fill-[var(--my-color)] hover:fill-[var(--my-color-2)] hover:focus:fill-[var(--my-color-3)]"></div>
210+
`)
211+
212+
expect(extractions).toContain('fill-[var(--my-color)]')
213+
expect(extractions).toContain('hover:fill-[var(--my-color-2)]')
214+
expect(extractions).toContain('hover:focus:fill-[var(--my-color-3)]')
215+
})
216+
217+
test('arbitrary values with type hints', async () => {
218+
const extractions = defaultExtractor(`
219+
<div class="text-[color:var(--my-color)] hover:text-[color:var(--my-color-2)] hover:focus:text-[color:var(--my-color-3)]"></div>
220+
`)
221+
222+
expect(extractions).toContain('text-[color:var(--my-color)]')
223+
expect(extractions).toContain('hover:text-[color:var(--my-color-2)]')
224+
expect(extractions).toContain('hover:focus:text-[color:var(--my-color-3)]')
225+
})
226+
227+
test('arbitrary values with single quotes', async () => {
228+
const extractions = defaultExtractor(`
229+
<div class="content-['hello_world'] hover:content-['hello_world_2'] hover:focus:content-['hello_world_3']"></div>
230+
`)
231+
232+
expect(extractions).toContain(`content-['hello_world']`)
233+
expect(extractions).toContain(`hover:content-['hello_world_2']`)
234+
expect(extractions).toContain(`hover:focus:content-['hello_world_3']`)
235+
})
236+
237+
test('arbitrary values with double quotes', async () => {
238+
const extractions = defaultExtractor(`
239+
<div class='content-["hello_world"] hover:content-["hello_world_2"] hover:focus:content-["hello_world_3"]'></div>
240+
`)
241+
242+
expect(extractions).toContain(`content-["hello_world"]`)
243+
expect(extractions).toContain(`hover:content-["hello_world_2"]`)
244+
expect(extractions).toContain(`hover:focus:content-["hello_world_3"]`)
245+
})
246+
247+
test('arbitrary values with some single quoted values', async () => {
248+
const extractions = defaultExtractor(`
249+
<div class="font-['Open_Sans',_system-ui,_sans-serif] hover:font-['Proxima_Nova',_system-ui,_sans-serif] hover:focus:font-['Inter_var',_system-ui,_sans-serif]"></div>
250+
`)
251+
252+
expect(extractions).toContain(`font-['Open_Sans',_system-ui,_sans-serif]`)
253+
expect(extractions).toContain(`hover:font-['Proxima_Nova',_system-ui,_sans-serif]`)
254+
expect(extractions).toContain(`hover:focus:font-['Inter_var',_system-ui,_sans-serif]`)
255+
})
256+
257+
test('arbitrary values with some double quoted values', async () => {
258+
const extractions = defaultExtractor(`
259+
<div class='font-["Open_Sans",_system-ui,_sans-serif] hover:font-["Proxima_Nova",_system-ui,_sans-serif] hover:focus:font-["Inter_var",_system-ui,_sans-serif]'></div>
260+
`)
261+
262+
expect(extractions).toContain(`font-["Open_Sans",_system-ui,_sans-serif]`)
263+
expect(extractions).toContain(`hover:font-["Proxima_Nova",_system-ui,_sans-serif]`)
264+
expect(extractions).toContain(`hover:focus:font-["Inter_var",_system-ui,_sans-serif]`)
265+
})
266+
267+
test('arbitrary values with escaped underscores', async () => {
268+
const extractions = defaultExtractor(`
269+
<div class="content-['hello\\_world'] hover:content-['hello\\_world\\_2'] hover:focus:content-['hello\\_world\\_3']"></div>
270+
`)
271+
272+
expect(extractions).toContain(`content-['hello\\_world']`)
273+
expect(extractions).toContain(`hover:content-['hello\\_world\\_2']`)
274+
expect(extractions).toContain(`hover:focus:content-['hello\\_world\\_3']`)
275+
})
276+
277+
test('basic utilities with arbitrary color opacity modifier', async () => {
278+
const extractions = defaultExtractor(`
279+
<div class="text-red-500/[.25] hover:text-red-500/[.5] hover:active:text-red-500/[.75]"></div>
280+
`)
281+
282+
expect(extractions).toContain('text-red-500/[.25]')
283+
expect(extractions).toContain('hover:text-red-500/[.5]')
284+
expect(extractions).toContain('hover:active:text-red-500/[.75]')
285+
})
286+
287+
test('arbitrary values with arbitrary color opacity modifier', async () => {
288+
const extractions = defaultExtractor(`
289+
<div class="text-[#bada55]/[.25] hover:text-[#bada55]/[.5] hover:active:text-[#bada55]/[.75]"></div>
290+
`)
291+
292+
expect(extractions).toContain('text-[#bada55]/[.25]')
293+
expect(extractions).toContain('hover:text-[#bada55]/[.5]')
294+
expect(extractions).toContain('hover:active:text-[#bada55]/[.75]')
295+
})
296+
297+
test('arbitrary values with angle brackets', async () => {
298+
const extractions = defaultExtractor(`
299+
<div class="content-[>] hover:content-[<] hover:focus:content-[>]"></div>
300+
`)
301+
302+
expect(extractions).toContain(`content-[>]`)
303+
expect(extractions).toContain(`hover:content-[<]`)
304+
expect(extractions).toContain(`hover:focus:content-[>]`)
305+
})
306+
307+
test('arbitrary values with angle brackets in single quotes', async () => {
308+
const extractions = defaultExtractor(`
309+
<div class="content-['>'] hover:content-['<'] hover:focus:content-['>']"></div>
310+
`)
311+
312+
expect(extractions).toContain(`content-['>']`)
313+
expect(extractions).toContain(`hover:content-['<']`)
314+
expect(extractions).toContain(`hover:focus:content-['>']`)
315+
})
316+
317+
test('arbitrary values with angle brackets in double quotes', async () => {
318+
const extractions = defaultExtractor(`
319+
<div class="content-[">"] hover:content-["<"] hover:focus:content-[">"]"></div>
320+
`)
321+
322+
expect(extractions).toContain(`content-[">"]`)
323+
expect(extractions).toContain(`hover:content-["<"]`)
324+
expect(extractions).toContain(`hover:focus:content-[">"]`)
325+
})

0 commit comments

Comments
 (0)