11const fs = require ( 'fs' )
2+ const path = require ( 'path' )
23const fastGlob = require ( 'fast-glob' )
34const sharedState = require ( './sharedState' )
45const { generateRules } = require ( './generateRules' )
@@ -7,10 +8,39 @@ const { bigSign } = require('./utils')
78let env = sharedState . env
89let contentMatchCache = sharedState . contentMatchCache
910
11+ const BROAD_MATCH_GLOBAL_REGEXP = / [ ^ < > " ' ` \s ] * [ ^ < > " ' ` \s : ] / g
12+ const INNER_MATCH_GLOBAL_REGEXP = / [ ^ < > " ' ` \s . ( ) { } [ \] # = % ] * [ ^ < > " ' ` \s . ( ) { } [ \] # = % : ] / g
13+
14+ function defaultJitExtractor ( content ) {
15+ let broadMatches = content . match ( BROAD_MATCH_GLOBAL_REGEXP ) || [ ]
16+ let innerMatches = content . match ( INNER_MATCH_GLOBAL_REGEXP ) || [ ]
17+
18+ return [ ...broadMatches , ...innerMatches ]
19+ }
20+
21+ function getExtractor ( fileName , tailwindConfig ) {
22+ const purgeOptions = tailwindConfig && tailwindConfig . purge && tailwindConfig . purge . options
23+
24+ if ( ! purgeOptions ) {
25+ return defaultJitExtractor
26+ }
27+
28+ const fileExtension = path . extname ( fileName ) . slice ( 1 )
29+ const fileSpecificExtractor = ( purgeOptions . extractors || [ ] ) . find ( ( extractor ) =>
30+ extractor . extensions . includes ( fileExtension )
31+ )
32+
33+ if ( fileSpecificExtractor ) {
34+ return fileSpecificExtractor . extractor
35+ }
36+
37+ return purgeOptions . defaultExtractor || defaultJitExtractor
38+ }
39+
1040// Scans template contents for possible classes. This is a hot path on initial build but
1141// not too important for subsequent builds. The faster the better though — if we can speed
1242// up these regexes by 50% that could cut initial build time by like 20%.
13- function getClassCandidates ( content , contentMatchCache , candidates , seen ) {
43+ function getClassCandidates ( content , extractor , contentMatchCache , candidates , seen ) {
1444 for ( let line of content . split ( '\n' ) ) {
1545 line = line . trim ( )
1646
@@ -24,20 +54,14 @@ function getClassCandidates(content, contentMatchCache, candidates, seen) {
2454 candidates . add ( match )
2555 }
2656 } else {
27- let allMatches = new Set ( )
28- let broadMatches = line . match ( / [ ^ < > " ' ` \s ] * [ ^ < > " ' ` \s : ] / g) || [ ]
29- let innerMatches = line . match ( / [ ^ < > " ' ` \s . ( ) { } [ \] # = % ] * [ ^ < > " ' ` \s . ( ) { } [ \] # = % : ] / g) || [ ]
57+ let extractorMatches = extractor ( line )
58+ let lineMatchesSet = new Set ( extractorMatches )
3059
31- for ( let match of broadMatches ) {
32- allMatches . add ( match )
33- candidates . add ( match )
34- }
35- for ( let match of innerMatches ) {
36- allMatches . add ( match )
60+ for ( let match of lineMatchesSet ) {
3761 candidates . add ( match )
3862 }
3963
40- contentMatchCache . set ( line , allMatches )
64+ contentMatchCache . set ( line , lineMatchesSet )
4165 }
4266 }
4367}
@@ -143,7 +167,8 @@ function expandTailwindAtRules(context, registerDependency) {
143167 env . DEBUG && console . time ( 'Reading changed files' )
144168 for ( let file of context . changedFiles ) {
145169 let content = fs . readFileSync ( file , 'utf8' )
146- getClassCandidates ( content , contentMatchCache , candidates , seen )
170+ let extractor = getExtractor ( file , context . tailwindConfig )
171+ getClassCandidates ( content , extractor , contentMatchCache , candidates , seen )
147172 }
148173 env . DEBUG && console . timeEnd ( 'Reading changed files' )
149174
0 commit comments