-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Properly extract classes with arbitrary values in arrays and classes …
…followed by escaped quotes (#6590) Co-authored-by: Robin Malfait <[email protected]>
- Loading branch information
1 parent
2fdbe10
commit 27c67fe
Showing
4 changed files
with
142 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
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, // bg-[url('...')] | ||
/([^<>"'`\s]*\[\w*\("[^"'`\s]*"\)\])/.source, // bg-[url("...")] | ||
/([^<>"'`\s]*\[\w*\('[^"`\s]*'\)\])/.source, // bg-[url('...'),url('...')] | ||
/([^<>"'`\s]*\[\w*\("[^'`\s]*"\)\])/.source, // bg-[url("..."),url("...")] | ||
/([^<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']` | ||
/([^<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]` | ||
/([^<>"'`\s]*\[[^<>"'`\s]*:'[^"'`\s]*'\])/.source, // `[content:'hello']` but not `[content:"hello"]` | ||
/([^<>"'`\s]*\[[^<>"'`\s]*:"[^"'`\s]*"\])/.source, // `[content:"hello"]` but not `[content:'hello']` | ||
/([^<>"'`\s]*\[[^"'`\s]+\][^<>"'`\s]*)/.source, // `fill-[#bada55]`, `fill-[#bada55]/50` | ||
/([^<>"'`\s]*[^"'`\s:\\])/.source, // `px-1.5`, `uppercase` but not `uppercase:` | ||
].join('|') | ||
|
||
const BROAD_MATCH_GLOBAL_REGEXP = new RegExp(PATTERNS, 'g') | ||
const INNER_MATCH_GLOBAL_REGEXP = /[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g | ||
|
||
/** | ||
* @param {string} content | ||
*/ | ||
export function defaultExtractor(content) { | ||
let broadMatches = content.matchAll(BROAD_MATCH_GLOBAL_REGEXP) | ||
let innerMatches = content.match(INNER_MATCH_GLOBAL_REGEXP) || [] | ||
let results = [...broadMatches, ...innerMatches].flat().filter((v) => v !== undefined) | ||
|
||
return results | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { html } from './util/run' | ||
import { defaultExtractor } from '../src/lib/defaultExtractor' | ||
|
||
const input = html` | ||
<div class="font-['some_font',sans-serif]"></div> | ||
<div class='font-["some_font",sans-serif]'></div> | ||
<div class="bg-[url('...')]"></div> | ||
<div class="bg-[url("...")]"></div> | ||
<div class="bg-[url('...'),url('...')]"></div> | ||
<div class="bg-[url("..."),url("...")]"></div> | ||
<div class="content-['hello']"></div> | ||
<div class="content-['hello']']"></div> | ||
<div class="content-["hello"]"></div> | ||
<div class="content-["hello"]"]"></div> | ||
<div class="[content:'hello']"></div> | ||
<div class="[content:"hello"]"></div> | ||
<div class="[content:"hello"]"></div> | ||
<div class="[content:'hello']"></div> | ||
<div class="fill-[#bada55]"></div> | ||
<div class="fill-[#bada55]/50"></div> | ||
<div class="px-1.5"></div> | ||
<div class="uppercase"></div> | ||
<div class="uppercase:"></div> | ||
<div class="hover:font-bold"></div> | ||
<div class="content-['>']"></div> | ||
<script> | ||
let classes01 = ["text-[10px]"] | ||
let classes02 = ["hover:font-bold"] | ||
let classes03 = {"code": "<div class=\"text-sm text-blue-500\"></div>"} | ||
let classes04 = ['text-[11px]'] | ||
let classes05 = ['text-[21px]', 'text-[22px]', 'lg:text-[24px]'] | ||
let classes06 = ["text-[31px]", "text-[32px]"] | ||
let classes07 = [${'`'}text-[41px]${'`'}, ${'`'}text-[42px]${'`'}] | ||
let classes08 = {"text-[51px]":"text-[52px]"} | ||
let classes09 = {'text-[61px]':'text-[62px]'} | ||
let classes10 = {${'`'}text-[71px]${'`'}:${'`'}text-[72px]${'`'}} | ||
let classes11 = ['hover:'] | ||
let classes12 = ['hover:\'abc'] | ||
let classes13 = ["lg:text-[4px]"] | ||
let classes14 = ["<div class='hover:test'>"] | ||
let obj = { | ||
uppercase:true | ||
} | ||
</script> | ||
` | ||
|
||
const includes = [ | ||
`font-['some_font',sans-serif]`, | ||
`font-["some_font",sans-serif]`, | ||
`bg-[url('...')]`, | ||
`bg-[url("...")]`, | ||
`bg-[url('...'),url('...')]`, | ||
`bg-[url("..."),url("...")]`, | ||
`content-['hello']`, | ||
`content-["hello"]`, | ||
`[content:'hello']`, | ||
`[content:"hello"]`, | ||
`[content:"hello"]`, | ||
`[content:'hello']`, | ||
`fill-[#bada55]`, | ||
`fill-[#bada55]/50`, | ||
`px-1.5`, | ||
`uppercase`, | ||
`hover:font-bold`, | ||
`text-sm`, | ||
`text-[10px]`, | ||
`text-[11px]`, | ||
`text-blue-500`, | ||
`text-[21px]`, | ||
`text-[22px]`, | ||
`text-[31px]`, | ||
`text-[32px]`, | ||
`text-[41px]`, | ||
`text-[42px]`, | ||
`text-[51px]`, | ||
`text-[52px]`, | ||
`text-[61px]`, | ||
`text-[62px]`, | ||
`text-[71px]`, | ||
`text-[72px]`, | ||
`lg:text-[4px]`, | ||
`lg:text-[24px]`, | ||
`content-['>']`, | ||
`hover:test`, | ||
] | ||
|
||
const excludes = [ | ||
`uppercase:`, | ||
'hover:', | ||
"hover:'abc", | ||
`font-bold`, | ||
`<div class='hover:test'>`, | ||
`test`, | ||
] | ||
|
||
test('The default extractor works as expected', async () => { | ||
const extractions = defaultExtractor(input.trim()) | ||
|
||
for (const str of includes) { | ||
expect(extractions).toContain(str) | ||
} | ||
|
||
for (const str of excludes) { | ||
expect(extractions).not.toContain(str) | ||
} | ||
}) |