1- use std:: {
2- collections:: { HashMap , HashSet } ,
3- env, fs,
4- path:: { Path , PathBuf } ,
5- process,
6- } ;
1+ use std:: { env, process} ;
72
83use anyhow:: Result ;
9- use config :: get_compiler_options ;
10- use css_parser :: { extract_classes , ClassName } ;
11- use tsx_parser :: { extract_default_css_imports , extract_used_classes , UsedClassName } ;
12- use utils :: { process_relative_import , replace_aliases } ;
4+ use modules :: {
5+ css_class :: get_class_body , defined_classes :: get_defined_classes , linter ,
6+ styles_imports :: get_styles_imports ,
7+ } ;
138
149mod config;
15- mod css_parser ;
16- mod tsx_parser ;
10+ mod modules ;
11+ mod parsers ;
1712mod utils;
1813
19- fn list_files_in_directory ( path : PathBuf , exclude : Vec < String > ) -> Vec < String > {
20- let mut files = Vec :: new ( ) ;
21-
22- if let Ok ( entries) = fs:: read_dir ( & path) {
23- for entry in entries. flatten ( ) {
24- let path = entry. path ( ) ;
25-
26- if path. is_dir ( ) {
27- if let Some ( p) = path. file_name ( ) {
28- let p_str = p. to_string_lossy ( ) ;
29- if p_str. starts_with ( '.' ) || exclude. iter ( ) . any ( |i| p_str == * i) {
30- continue ;
31- }
32- }
33- files. extend ( list_files_in_directory ( path, exclude. clone ( ) ) ) ;
34- } else if path. is_file ( ) {
35- if let Some ( path_str) = path. to_str ( ) {
36- files. push ( path_str. to_string ( ) ) ;
37- }
38- }
39- }
40- } else {
41- eprintln ! ( "Cannot open target dir: {:?}" , path) ;
42- }
43-
44- files
45- }
46-
4714fn main ( ) -> Result < ( ) > {
48- const COLOR_BLUE : & str = "\x1b [34m" ;
49- const COLOR_YELLOW : & str = "\x1b [33m" ;
50- const COLOR_GREEN : & str = "\x1b [32m" ;
5115 const COLOR_RED : & str = "\x1b [31m" ;
5216 const COLOR_RESET : & str = "\u{001B} [0m" ;
5317
5418 let args: Vec < String > = env:: args ( ) . collect ( ) ;
19+ if let Ok ( cwd) = std:: env:: var ( "cwd" ) {
20+ env:: set_current_dir ( cwd) ?;
21+ }
5522
56- let path = match args. get ( 1 ) {
23+ match args. get ( 1 ) {
5724 Some ( arg) if arg == "-v" => {
58- println ! ( "v{}" , env!( "CARGO_PKG_VERSION" ) ) ;
25+ print ! ( "v{}" , env!( "CARGO_PKG_VERSION" ) ) ;
5926 process:: exit ( 0 ) ;
6027 }
61- Some ( arg) if arg == "--lint" => args. get ( 2 ) . unwrap_or_else ( || {
62- eprintln ! (
63- "\n {}Error{}: Linting path must be specified" ,
64- COLOR_RED , COLOR_RESET
65- ) ;
66- process:: exit ( 1 ) ;
67- } ) ,
28+ Some ( arg) if arg == "--lint" => linter:: lint ( ) ?,
29+ Some ( arg) if arg == "--imports" => get_styles_imports ( ) ?,
30+ Some ( arg) if arg == "--classes" => get_defined_classes ( ) ?,
31+ Some ( arg) if arg == "--class" => get_class_body ( ) ?,
6832 Some ( arg) => {
6933 eprintln ! (
7034 "{}Error{}: Invalid argument: {}" ,
@@ -75,178 +39,5 @@ fn main() -> Result<()> {
7539 None => todo ! ( ) ,
7640 } ;
7741
78- let minify = args. get ( 3 ) . map_or ( "" , |v| v) == "--minify" ;
79-
80- if let Err ( e) = env:: set_current_dir ( Path :: new ( path) ) {
81- eprintln ! (
82- "\n {}Error{}: Failed to set current directory: {}" ,
83- COLOR_RED , COLOR_RESET , e
84- ) ;
85- process:: exit ( 1 ) ;
86- }
87- let tsconfig = get_compiler_options ( ) . unwrap_or_else ( |e| {
88- eprintln ! (
89- "\n {}Error{}: Could not load tsconfig.json. Is the provided directory a typescript project? ({})" ,
90- COLOR_RED , COLOR_RESET , e
91- ) ;
92- process:: exit ( 1 ) ;
93- } ) ;
94-
95- let dir = list_files_in_directory ( Path :: new ( "." ) . to_path_buf ( ) , tsconfig. exclude ) ;
96-
97- let mut used_classnames: HashMap < String , HashSet < UsedClassName > > = Default :: default ( ) ;
98- let mut defined_classnames: HashMap < String , HashSet < ClassName > > = Default :: default ( ) ;
99-
100- for entry in & dir {
101- let path = entry. replace ( "\\ " , "/" ) ;
102-
103- if path. ends_with ( ".tsx" ) {
104- let code = fs:: read_to_string ( entry) ?;
105- let imported_css = extract_default_css_imports ( & code) . unwrap_or_else ( |e| {
106- eprintln ! ( "Could not parse file: {}\n {}" , entry, e) ;
107- process:: exit ( 1 ) ;
108- } ) ;
109-
110- for ( mut style_path, class_names) in imported_css {
111- process_relative_import ( Path :: new ( entry) , & mut style_path) ?;
112- replace_aliases ( & mut style_path, tsconfig. compiler_options . paths . clone ( ) ) ;
113-
114- let used_fields = extract_used_classes ( & code, & class_names, path. clone ( ) )
115- . unwrap_or_else ( |e| {
116- eprintln ! ( "Could not parse file: {}\n {}" , entry, e) ;
117- process:: exit ( 1 ) ;
118- } ) ;
119-
120- used_classnames
121- . entry ( style_path)
122- . or_insert_with ( HashSet :: new)
123- . extend ( used_fields) ;
124- }
125- } else if path. ends_with ( ".module.css" ) {
126- let code = fs:: read_to_string ( entry) ?;
127- let classes = extract_classes ( & code) ;
128- defined_classnames
129- . entry ( path)
130- . or_insert_with ( HashSet :: new)
131- . extend ( classes) ;
132- }
133- }
134-
135- let mut files_count = 0 ;
136- let mut errors_count = 0 ;
137-
138- for ( css_file, mut classes_tsx) in defined_classnames. clone ( ) {
139- if let Some ( used_css) = used_classnames. get ( & css_file) {
140- let used_css_flatten: Vec < String > =
141- used_css. iter ( ) . map ( |v| v. class_name . clone ( ) ) . collect ( ) ;
142- classes_tsx. retain ( |v| !used_css_flatten. contains ( & v. class_name ) ) ;
143- }
144-
145- if classes_tsx. is_empty ( ) {
146- continue ;
147- }
148-
149- files_count += 1 ;
150- errors_count += classes_tsx. len ( ) ;
151-
152- if !minify {
153- println ! ( "{}{}{}" , COLOR_BLUE , css_file, COLOR_RESET ) ;
154- }
155-
156- for extra in classes_tsx {
157- if !minify {
158- println ! (
159- "{}{}:{} {}Warn{}: Unused class `{}` found." ,
160- COLOR_YELLOW ,
161- extra. line_index + 1 ,
162- extra. column_index + 1 ,
163- COLOR_YELLOW ,
164- COLOR_RESET ,
165- extra. class_name
166- ) ;
167- } else {
168- println ! (
169- "{}:{}:{}:{}:\" {}\" : Unused class found." ,
170- css_file,
171- extra. line_index + 1 ,
172- extra. column_index + 1 ,
173- extra. class_name. len( ) ,
174- extra. class_name
175- ) ;
176- }
177- }
178-
179- if !minify {
180- println ! ( ) ;
181- }
182- }
183-
184- let mut undefined_classes: HashMap < String , HashSet < UsedClassName > > = HashMap :: new ( ) ;
185-
186- for ( tsx_file, mut classes) in used_classnames {
187- if let Some ( defined_css) = defined_classnames. get ( & tsx_file) {
188- let defined_css: HashSet < String > =
189- defined_css. iter ( ) . map ( |v| v. class_name . clone ( ) ) . collect ( ) ;
190- classes. retain ( |v| !defined_css. contains ( v. class_name . as_str ( ) ) ) ;
191- }
192-
193- if classes. is_empty ( ) {
194- continue ;
195- }
196-
197- files_count += 1 ;
198- errors_count += classes. len ( ) ;
199-
200- for extra in classes {
201- undefined_classes
202- . entry ( extra. file_name . clone ( ) )
203- . or_insert_with ( HashSet :: new)
204- . insert ( extra) ;
205- }
206- }
207-
208- for undefined in undefined_classes {
209- if !minify {
210- println ! ( "{}{}{}" , COLOR_BLUE , undefined. 0 , COLOR_RESET ) ;
211- }
212- for extra in undefined. 1 {
213- if !minify {
214- println ! (
215- "{}{}:{} {}Warn{}: Undefined class `{}` was used." ,
216- COLOR_YELLOW ,
217- extra. line,
218- extra. column + 1 ,
219- COLOR_YELLOW ,
220- COLOR_RESET ,
221- extra. class_name
222- ) ;
223- } else {
224- println ! (
225- "{}:{}:{}:{}:\" {}\" : Undefined class was used." ,
226- undefined. 0 ,
227- extra. line,
228- extra. column + 1 ,
229- extra. class_name. len( ) ,
230- extra. class_name
231- ) ;
232- }
233- }
234-
235- if !minify {
236- println ! ( ) ;
237- }
238- }
239-
240- if !minify {
241- if errors_count == 0 {
242- println ! ( "{}✔{} No CSS lint warnings found" , COLOR_GREEN , COLOR_RESET ) ;
243- } else {
244- println ! (
245- "Found {}{} warnings{} in {} files" ,
246- COLOR_YELLOW , errors_count, COLOR_RESET , files_count
247- ) ;
248- }
249- }
250-
25142 Ok ( ( ) )
25243}
0 commit comments