Skip to content

Commit

Permalink
Fix extraction from template literal/function with array (#7481)
Browse files Browse the repository at this point in the history
* 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 <jordan@cryptica.me>
  • Loading branch information
SamuelAlev and thecrypticace committed Mar 1, 2022
1 parent 7df3d93 commit bc46d0e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Invalidate context when main CSS changes ([#7626](https://github.com/tailwindlabs/tailwindcss/pull/7626))
- Only add `!` to selector class matching template candidate when using important modifier with mutli-class selectors ([#7664](https://github.com/tailwindlabs/tailwindcss/pull/7664))
- Correctly parse and prefix animation names with dots ([#7163](https://github.com/tailwindlabs/tailwindcss/pull/7163))
- Fix extraction from template literal/function with array ([#7481](https://github.com/tailwindlabs/tailwindcss/pull/7481))

### Changed

Expand Down
8 changes: 4 additions & 4 deletions src/lib/defaultExtractor.js
Expand Up @@ -2,16 +2,16 @@ const PATTERNS = [
/(?:\['([^'\s]+[^<>"'`\s:\\])')/.source, // ['text-lg' -> text-lg
/(?:\["([^"\s]+[^<>"'`\s:\\])")/.source, // ["text-lg" -> text-lg
/(?:\[`([^`\s]+[^<>"'`\s:\\])`)/.source, // [`text-lg` -> text-lg
/([^<>"'`\s]*\[\w*'[^"`\s]*'?\])/.source, // font-['some_font',sans-serif]
/([^<>"'`\s]*\[\w*"[^'`\s]*"?\])/.source, // font-["some_font",sans-serif]
/([^${(<>"'`\s]*\[\w*'[^"`\s]*'?\])/.source, // font-['some_font',sans-serif]
/([^${(<>"'`\s]*\[\w*"[^'`\s]*"?\])/.source, // font-["some_font",sans-serif]
/([^<>"'`\s]*\[\w*\('[^"'`\s]*'\)\])/.source, // bg-[url('...')]
/([^<>"'`\s]*\[\w*\("[^"'`\s]*"\)\])/.source, // bg-[url("...")]
/([^<>"'`\s]*\[\w*\('[^"`\s]*'\)\])/.source, // bg-[url('...'),url('...')]
/([^<>"'`\s]*\[\w*\("[^'`\s]*"\)\])/.source, // bg-[url("..."),url("...")]
/([^<>"'`\s]*\[[^<>"'`\s]*\('[^"`\s]*'\)+\])/.source, // h-[calc(100%-theme('spacing.1'))]
/([^<>"'`\s]*\[[^<>"'`\s]*\("[^'`\s]*"\)+\])/.source, // h-[calc(100%-theme("spacing.1"))]
/([^<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']`
/([^<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]`
/([^${(<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']`
/([^${(<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]`
/([^<>"'`\s]*\[[^<>"'`\s]*:[^\]\s]*\])/.source, // `[attr:value]`
/([^<>"'`\s]*\[[^<>"'`\s]*:'[^"'`\s]*'\])/.source, // `[content:'hello']` but not `[content:"hello"]`
/([^<>"'`\s]*\[[^<>"'`\s]*:"[^"'`\s]*"\])/.source, // `[content:"hello"]` but not `[content:'hello']`
Expand Down
84 changes: 78 additions & 6 deletions tests/default-extractor.test.js
@@ -1,9 +1,22 @@
import { html } from './util/run'
import { defaultExtractor } from '../src/lib/defaultExtractor'

let jsxExample = "<div className={`overflow-scroll${conditionIsOpen ? '' : ' hidden'}`}></div>"
const input =
html`
const jsExamples = `
document.body.classList.add(["pl-1.5"].join(" "));
document.body.classList.add(['pl-2.5'].join(" "));
`
const jsxExamples = `
<div className={\`overflow-scroll\${conditionIsOpen ? '' : ' hidden'}\`}></div>
<div className={\`\${['pr-1.5'].join(' ')}\`}><div>
<div className={\`\${['pr-2.5', 'pr-3.5'].join(' ')}\`}><div>
<div className={\`\${["pr-4.5"].join(' ')}\`}><div>
<div className={\`\${["pr-5.5", "pr-6.5"].join(' ')}\`}><div>
<div className={\`\${['h-[100px]'].join(' ')}\`}><div>
<div className={\`\${['h-[101px]', 'h-[102px]'].join(' ')}\`}><div>
<div className={\`\${["h-[103px]"].join(' ')}\`}><div>
<div className={\`\${["h-[104px]", "h-[105px]"].join(' ')}\`}><div>
`
const htmlExamples = html`
<div class="font-['some_font',sans-serif]"></div>
<div class='font-["some_font",sans-serif]'></div>
<div class="bg-[url('...')]"></div>
Expand Down Expand Up @@ -46,10 +59,21 @@ const input =
let classes14 = ["<div class='hover:test'>"]
let obj = {
lowercase: true
lowercase: true,
"normal-case": true,
'ml-0.5': true,
'ml-0.5': true,
'h-[106px]': true,
"h-[107px]": true,
}
let obj2 = {
'h-[108px]': true
}
let obj3 = {
"h-[109px]": true
}
</script>
` + jsxExample
`

const includes = [
`font-['some_font',sans-serif]`,
Expand All @@ -67,8 +91,28 @@ const includes = [
`fill-[#bada55]`,
`fill-[#bada55]/50`,
`px-1.5`,
`pl-1.5`,
`pl-2.5`,
`pr-1.5`,
`pr-2.5`,
`pr-3.5`,
`pr-4.5`,
`pr-5.5`,
`pr-6.5`,
`ml-0.5`,
`h-[100px]`,
`h-[101px]`,
`h-[102px]`,
`h-[103px]`,
`h-[104px]`,
`h-[105px]`,
`h-[106px]`,
`h-[107px]`,
`h-[108px]`,
`h-[109px]`,
`uppercase`,
`lowercase`,
`normal-case`,
`hover:font-bold`,
`text-sm`,
`text-[10px]`,
Expand Down Expand Up @@ -106,7 +150,7 @@ const excludes = [
]

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

for (const str of includes) {
expect(extractions).toContain(str)
Expand Down Expand Up @@ -345,3 +389,31 @@ test('special characters', async () => {
expect(extractions).toContain(`<sm:underline`)
expect(extractions).toContain(`md>:font-bold`)
})

test('with single quotes array within template literal', async () => {
const extractions = defaultExtractor(`<div class=\`\${['pr-1.5']}\`></div>`)

expect(extractions).toContain('pr-1.5')
expect(extractions).toContain('pr-1')
})

test('with double quotes array within template literal', async () => {
const extractions = defaultExtractor(`<div class=\`\${["pr-1.5"]}\`></div>`)

expect(extractions).toContain('pr-1.5')
expect(extractions).toContain('pr-1')
})

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')
expect(extractions).toContain('pl-1')
})

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')
expect(extractions).toContain('pl-1')
})

0 comments on commit bc46d0e

Please sign in to comment.