1+ const fs = require ( 'fs' ) ;
2+ const path = require ( 'path' ) ;
3+
4+ const cheerio = require ( 'cheerio' ) ;
5+ const recursiveReadDir = require ( 'recursive-readdir' ) ;
6+
7+ const STYLESHEET_REGEXP = / { { \s * s t y l e s h e e t \s * ( [ \/ a - z A - Z ' " \. - ] + ) \s * } } / gi;
8+
9+ class StylesheetsFileResolver {
10+ constructor ( themePath ) {
11+ this . themePath = themePath ;
12+ }
13+
14+ async get ( ) {
15+ const templatesPath = path . join ( this . themePath , 'templates' ) ;
16+ const files = await recursiveReadDir ( templatesPath ) ;
17+ const cssFiles = [ ] ;
18+ for await ( const file of files ) {
19+ const content = await fs . promises . readFile ( file , { encoding : 'utf-8' } ) ;
20+ const result = content . matchAll ( STYLESHEET_REGEXP ) ;
21+ if ( result ) {
22+ for ( const item of result ) {
23+ // remove quotes
24+ const filePath = item [ 1 ] . slice ( 1 , - 1 ) ;
25+ const fileName = this . tryToResolveCssFileLocation ( filePath ) ;
26+ if (
27+ fileName &&
28+ ! this . isStyleSheetAComment ( content , filePath ) &&
29+ ! cssFiles . includes ( fileName )
30+ ) {
31+ cssFiles . push ( fileName ) ;
32+ }
33+ }
34+ }
35+ }
36+
37+ return cssFiles ;
38+ }
39+
40+ isStyleSheetAComment ( content , cssFilePath ) {
41+ const $ = cheerio . load ( content ) ;
42+ const comments = $ ( '*' )
43+ . contents ( )
44+ . filter ( function ( ) {
45+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#node.comment_node
46+ return this . nodeType === 8 ;
47+ } ) ;
48+ for ( const comment of comments ) {
49+ const { data } = comment ;
50+ if ( data && data . includes ( 'stylesheet' ) && data . includes ( cssFilePath ) ) {
51+ return true ;
52+ }
53+ }
54+
55+ return false ;
56+ }
57+
58+ // returns relative path starting from root scss/css folder
59+ tryToResolveCssFileLocation ( filePath ) {
60+ const possibleLocations = [
61+ filePath ,
62+ filePath . replace ( '/css/' , '/scss/' ) ,
63+ filePath . replace ( '/scss/' , '/css/' ) ,
64+ filePath . replace ( '/css/' , '/scss/' ) . replace ( '.css' , '.scss' ) ,
65+ filePath . replace ( '/scss/' , '/css/' ) . replace ( '.scss' , '.css' ) ,
66+ ] ;
67+
68+ for ( const location of possibleLocations ) {
69+ const fullFilePath = path . join ( this . themePath , location ) ;
70+ if ( fs . existsSync ( fullFilePath ) ) {
71+ if ( fullFilePath . endsWith ( '.css' ) ) {
72+ return null ;
73+ }
74+ if ( ! this . isRootCssFile ( location ) ) {
75+ return this . getCssFileWithoutRootFolder ( location ) ;
76+ }
77+ const fileParts = path . parse ( fullFilePath ) ;
78+ return fileParts . name ;
79+ }
80+ }
81+
82+ console . log ( `Couldn't validate scss compilation for this file path: ${ filePath } ` . yellow ) ;
83+ return null ;
84+ }
85+
86+ // root folders are /assets/css /assets/scss
87+ // so after split, there can be 3 or 4 elements in the array (depending if the leading slash is present)
88+ isRootCssFile ( location ) {
89+ return location . split ( '/' ) . length <= 4 ;
90+ }
91+
92+ getCssFileWithoutRootFolder ( location ) {
93+ const locationParts = location . split ( '/' ) ;
94+ if ( locationParts [ 0 ] === '' ) {
95+ locationParts . shift ( ) ;
96+ }
97+ locationParts . shift ( ) ;
98+ locationParts . shift ( ) ;
99+
100+ return locationParts . join ( '/' ) ;
101+ }
102+
103+ }
104+
105+ module . exports = StylesheetsFileResolver ;
0 commit comments