1
1
const fs = require ( 'fs' )
2
+ const path = require ( 'path' )
2
3
const fastGlob = require ( 'fast-glob' )
3
4
const sharedState = require ( './sharedState' )
4
5
const { generateRules } = require ( './generateRules' )
@@ -7,10 +8,35 @@ const { bigSign } = require('./utils')
7
8
let env = sharedState . env
8
9
let contentMatchCache = sharedState . contentMatchCache
9
10
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 fileSpecificExtension = ( tailwindConfig . extractors || [ ] ) . find ( ( extractor ) =>
30
+ extractor . extensions . includes ( fileExtension )
31
+ )
32
+
33
+ return fileSpecificExtension || purgeOptions . defaultExtractor || defaultJitExtractor
34
+ }
35
+
10
36
// Scans template contents for possible classes. This is a hot path on initial build but
11
37
// not too important for subsequent builds. The faster the better though — if we can speed
12
38
// up these regexes by 50% that could cut initial build time by like 20%.
13
- function getClassCandidates ( content , contentMatchCache , candidates , seen ) {
39
+ function getClassCandidates ( content , extractor , contentMatchCache , candidates , seen ) {
14
40
for ( let line of content . split ( '\n' ) ) {
15
41
line = line . trim ( )
16
42
@@ -24,20 +50,14 @@ function getClassCandidates(content, contentMatchCache, candidates, seen) {
24
50
candidates . add ( match )
25
51
}
26
52
} else {
27
- let allMatches = new Set ( )
28
- let broadMatches = line . match ( / [ ^ < > " ' ` \s ] * [ ^ < > " ' ` \s : ] / g) || [ ]
29
- let innerMatches = line . match ( / [ ^ < > " ' ` \s . ( ) { } [ \] # = % ] * [ ^ < > " ' ` \s . ( ) { } [ \] # = % : ] / g) || [ ]
53
+ let extractorMatches = extractor ( line )
54
+ let lineMatchesSet = new Set ( extractorMatches )
30
55
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 )
56
+ for ( let match of lineMatchesSet ) {
37
57
candidates . add ( match )
38
58
}
39
59
40
- contentMatchCache . set ( line , allMatches )
60
+ contentMatchCache . set ( line , lineMatchesSet )
41
61
}
42
62
}
43
63
}
@@ -143,7 +163,8 @@ function expandTailwindAtRules(context, registerDependency) {
143
163
env . DEBUG && console . time ( 'Reading changed files' )
144
164
for ( let file of context . changedFiles ) {
145
165
let content = fs . readFileSync ( file , 'utf8' )
146
- getClassCandidates ( content , contentMatchCache , candidates , seen )
166
+ let extractor = getExtractor ( file , context . tailwindConfig )
167
+ getClassCandidates ( content , extractor , contentMatchCache , candidates , seen )
147
168
}
148
169
env . DEBUG && console . timeEnd ( 'Reading changed files' )
149
170
0 commit comments