33const fg = require ( 'fast-glob' ) ;
44const fs = require ( 'fs' ) ;
55const postcss = require ( 'postcss' ) ;
6+ const removeDuplicatesFromArray = require ( './removeDuplicatesFromArray' ) ;
67
7- const classRegexp = / \. ( [ ^ \. \, \s \n \: \( \) \[ \] \' ~ \+ \> \* \\ ] * ) / gim;
8-
8+ let previousGlobsResults = [ ] ;
99let lastUpdate = null ;
1010let classnamesFromFiles = [ ] ;
1111
12- /**
13- * @type {Map<string, number }
14- */
15- const prevEditedTimestamp = new Map ( )
16-
1712/**
1813 * Read CSS files and extract classnames
1914 * @param {Array } patterns Glob patterns to locate files
@@ -22,56 +17,27 @@ const prevEditedTimestamp = new Map()
2217 */
2318const generateClassnamesListSync = ( patterns , refreshRate = 5_000 ) => {
2419 const now = new Date ( ) . getTime ( ) ;
20+ const files = fg . sync ( patterns , { suppressErrors : true } ) ;
21+ const newGlobs = previousGlobsResults . flat ( ) . join ( ',' ) != files . flat ( ) . join ( ',' ) ;
2522 const expired = lastUpdate === null || now - lastUpdate > refreshRate ;
26-
27- if ( ! expired ) {
28- return classnamesFromFiles ;
29- }
30- const files = fg . sync ( patterns , { suppressErrors : true , stats : true } ) ;
31- lastUpdate = now ;
32-
33- /**
34- * @type {Set<string }
35- */
36- const detectedClassnames = new Set ( ) ;
37- /**
38- * @type {Set<string> }
39- */
40- const filesSet = new Set ( ) ;
41- for ( const { path : file , stats } of files ) {
42- filesSet . add ( file ) ;
43- if ( ! stats ) { }
44- // file is not changed -> we do need to do extra work
45- else if ( prevEditedTimestamp . get ( file ) === stats . mtimeMs ) {
46- continue ;
47- } else {
48- prevEditedTimestamp . set ( file , stats . mtimeMs ) ;
23+ if ( newGlobs || expired ) {
24+ previousGlobsResults = files ;
25+ lastUpdate = now ;
26+ let detectedClassnames = [ ] ;
27+ for ( const file of files ) {
28+ const data = fs . readFileSync ( file , 'utf-8' ) ;
29+ const root = postcss . parse ( data ) ;
30+ root . walkRules ( ( rule ) => {
31+ const regexp = / \. ( [ ^ \. \, \s \n \: \( \) \[ \] \' ~ \+ \> \* \\ ] * ) / gim;
32+ const matches = [ ...rule . selector . matchAll ( regexp ) ] ;
33+ const classnames = matches . map ( ( arr ) => arr [ 1 ] ) ;
34+ detectedClassnames . push ( ...classnames ) ;
35+ } ) ;
36+ detectedClassnames = removeDuplicatesFromArray ( detectedClassnames ) ;
4937 }
50- const data = fs . readFileSync ( file , 'utf-8' ) ;
51- const root = postcss . parse ( data ) ;
52- root . walkRules ( ( rule ) => {
53- for ( const match of rule . selector . matchAll ( classRegexp ) ) {
54- detectedClassnames . add ( match [ 1 ] ) ;
55- }
56- } ) ;
38+ classnamesFromFiles = detectedClassnames ;
5739 }
58- // avoiding memory leak
59- {
60- /**
61- * @type {string[] }
62- */
63- const keysToDelete = [ ]
64- for ( const cachedFilePath of prevEditedTimestamp . keys ( ) ) {
65- if ( ! filesSet . has ( cachedFilePath ) ) {
66- keysToDelete . push ( cachedFilePath ) ;
67- }
68- }
69- for ( const key of keysToDelete ) {
70- prevEditedTimestamp . delete ( key ) ;
71- }
72- }
73- classnamesFromFiles = [ ...detectedClassnames ] ;
74- return classnamesFromFiles
40+ return classnamesFromFiles ;
7541} ;
7642
7743module . exports = generateClassnamesListSync ;
0 commit comments