Skip to content

Commit bc46d0e

Browse files
Fix extraction from template literal/function with array (#7481)
* fix: allow extraction from template literal with array * fix: support extraction from array in function * test: add more tests for function and template * test: add test for dynamic classes * test: add dynamic class test in js * test: add dynamic class test in js single quote * Cleanup a bit * Update changelog Co-authored-by: Jordan Pittman <[email protected]>
1 parent 7df3d93 commit bc46d0e

File tree

3 files changed

+83
-10
lines changed

3 files changed

+83
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Invalidate context when main CSS changes ([#7626](https://github.com/tailwindlabs/tailwindcss/pull/7626))
1818
- Only add `!` to selector class matching template candidate when using important modifier with mutli-class selectors ([#7664](https://github.com/tailwindlabs/tailwindcss/pull/7664))
1919
- Correctly parse and prefix animation names with dots ([#7163](https://github.com/tailwindlabs/tailwindcss/pull/7163))
20+
- Fix extraction from template literal/function with array ([#7481](https://github.com/tailwindlabs/tailwindcss/pull/7481))
2021

2122
### Changed
2223

src/lib/defaultExtractor.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ const PATTERNS = [
22
/(?:\['([^'\s]+[^<>"'`\s:\\])')/.source, // ['text-lg' -> text-lg
33
/(?:\["([^"\s]+[^<>"'`\s:\\])")/.source, // ["text-lg" -> text-lg
44
/(?:\[`([^`\s]+[^<>"'`\s:\\])`)/.source, // [`text-lg` -> text-lg
5-
/([^<>"'`\s]*\[\w*'[^"`\s]*'?\])/.source, // font-['some_font',sans-serif]
6-
/([^<>"'`\s]*\[\w*"[^'`\s]*"?\])/.source, // font-["some_font",sans-serif]
5+
/([^${(<>"'`\s]*\[\w*'[^"`\s]*'?\])/.source, // font-['some_font',sans-serif]
6+
/([^${(<>"'`\s]*\[\w*"[^'`\s]*"?\])/.source, // font-["some_font",sans-serif]
77
/([^<>"'`\s]*\[\w*\('[^"'`\s]*'\)\])/.source, // bg-[url('...')]
88
/([^<>"'`\s]*\[\w*\("[^"'`\s]*"\)\])/.source, // bg-[url("...")]
99
/([^<>"'`\s]*\[\w*\('[^"`\s]*'\)\])/.source, // bg-[url('...'),url('...')]
1010
/([^<>"'`\s]*\[\w*\("[^'`\s]*"\)\])/.source, // bg-[url("..."),url("...")]
1111
/([^<>"'`\s]*\[[^<>"'`\s]*\('[^"`\s]*'\)+\])/.source, // h-[calc(100%-theme('spacing.1'))]
1212
/([^<>"'`\s]*\[[^<>"'`\s]*\("[^'`\s]*"\)+\])/.source, // h-[calc(100%-theme("spacing.1"))]
13-
/([^<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']`
14-
/([^<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]`
13+
/([^${(<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']`
14+
/([^${(<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]`
1515
/([^<>"'`\s]*\[[^<>"'`\s]*:[^\]\s]*\])/.source, // `[attr:value]`
1616
/([^<>"'`\s]*\[[^<>"'`\s]*:'[^"'`\s]*'\])/.source, // `[content:'hello']` but not `[content:"hello"]`
1717
/([^<>"'`\s]*\[[^<>"'`\s]*:"[^"'`\s]*"\])/.source, // `[content:"hello"]` but not `[content:'hello']`

tests/default-extractor.test.js

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
import { html } from './util/run'
22
import { defaultExtractor } from '../src/lib/defaultExtractor'
33

4-
let jsxExample = "<div className={`overflow-scroll${conditionIsOpen ? '' : ' hidden'}`}></div>"
5-
const input =
6-
html`
4+
const jsExamples = `
5+
document.body.classList.add(["pl-1.5"].join(" "));
6+
document.body.classList.add(['pl-2.5'].join(" "));
7+
`
8+
const jsxExamples = `
9+
<div className={\`overflow-scroll\${conditionIsOpen ? '' : ' hidden'}\`}></div>
10+
<div className={\`\${['pr-1.5'].join(' ')}\`}><div>
11+
<div className={\`\${['pr-2.5', 'pr-3.5'].join(' ')}\`}><div>
12+
<div className={\`\${["pr-4.5"].join(' ')}\`}><div>
13+
<div className={\`\${["pr-5.5", "pr-6.5"].join(' ')}\`}><div>
14+
<div className={\`\${['h-[100px]'].join(' ')}\`}><div>
15+
<div className={\`\${['h-[101px]', 'h-[102px]'].join(' ')}\`}><div>
16+
<div className={\`\${["h-[103px]"].join(' ')}\`}><div>
17+
<div className={\`\${["h-[104px]", "h-[105px]"].join(' ')}\`}><div>
18+
`
19+
const htmlExamples = html`
720
<div class="font-['some_font',sans-serif]"></div>
821
<div class='font-["some_font",sans-serif]'></div>
922
<div class="bg-[url('...')]"></div>
@@ -46,10 +59,21 @@ const input =
4659
let classes14 = ["<div class='hover:test'>"]
4760
4861
let obj = {
49-
lowercase: true
62+
lowercase: true,
63+
"normal-case": true,
64+
'ml-0.5': true,
65+
'ml-0.5': true,
66+
'h-[106px]': true,
67+
"h-[107px]": true,
68+
}
69+
let obj2 = {
70+
'h-[108px]': true
71+
}
72+
let obj3 = {
73+
"h-[109px]": true
5074
}
5175
</script>
52-
` + jsxExample
76+
`
5377

5478
const includes = [
5579
`font-['some_font',sans-serif]`,
@@ -67,8 +91,28 @@ const includes = [
6791
`fill-[#bada55]`,
6892
`fill-[#bada55]/50`,
6993
`px-1.5`,
94+
`pl-1.5`,
95+
`pl-2.5`,
96+
`pr-1.5`,
97+
`pr-2.5`,
98+
`pr-3.5`,
99+
`pr-4.5`,
100+
`pr-5.5`,
101+
`pr-6.5`,
102+
`ml-0.5`,
103+
`h-[100px]`,
104+
`h-[101px]`,
105+
`h-[102px]`,
106+
`h-[103px]`,
107+
`h-[104px]`,
108+
`h-[105px]`,
109+
`h-[106px]`,
110+
`h-[107px]`,
111+
`h-[108px]`,
112+
`h-[109px]`,
70113
`uppercase`,
71114
`lowercase`,
115+
`normal-case`,
72116
`hover:font-bold`,
73117
`text-sm`,
74118
`text-[10px]`,
@@ -106,7 +150,7 @@ const excludes = [
106150
]
107151

108152
test('The default extractor works as expected', async () => {
109-
const extractions = defaultExtractor(input.trim())
153+
const extractions = defaultExtractor([jsExamples, jsxExamples, htmlExamples].join('\n').trim())
110154

111155
for (const str of includes) {
112156
expect(extractions).toContain(str)
@@ -345,3 +389,31 @@ test('special characters', async () => {
345389
expect(extractions).toContain(`<sm:underline`)
346390
expect(extractions).toContain(`md>:font-bold`)
347391
})
392+
393+
test('with single quotes array within template literal', async () => {
394+
const extractions = defaultExtractor(`<div class=\`\${['pr-1.5']}\`></div>`)
395+
396+
expect(extractions).toContain('pr-1.5')
397+
expect(extractions).toContain('pr-1')
398+
})
399+
400+
test('with double quotes array within template literal', async () => {
401+
const extractions = defaultExtractor(`<div class=\`\${["pr-1.5"]}\`></div>`)
402+
403+
expect(extractions).toContain('pr-1.5')
404+
expect(extractions).toContain('pr-1')
405+
})
406+
407+
test('with single quotes array within function', async () => {
408+
const extractions = defaultExtractor(`document.body.classList.add(['pl-1.5'].join(" "));`)
409+
410+
expect(extractions).toContain('pl-1.5')
411+
expect(extractions).toContain('pl-1')
412+
})
413+
414+
test('with double quotes array within function', async () => {
415+
const extractions = defaultExtractor(`document.body.classList.add(["pl-1.5"].join(" "));`)
416+
417+
expect(extractions).toContain('pl-1.5')
418+
expect(extractions).toContain('pl-1')
419+
})

0 commit comments

Comments
 (0)