From a7b5bf61ce8bc89c7656fcccb20acedd1199d432 Mon Sep 17 00:00:00 2001 From: thepassle Date: Wed, 22 Feb 2023 09:57:12 +0100 Subject: [PATCH 1/3] feat: support usage of globs in cli mode --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/main.rs | 323 ++++++++++++++++++++++++++++------------------------ test.css | 3 + test2.css | 0 5 files changed, 184 insertions(+), 150 deletions(-) create mode 100644 test.css create mode 100644 test2.css diff --git a/Cargo.lock b/Cargo.lock index 54bbeb48..fbb7fb53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -479,6 +479,12 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "globset" version = "0.4.8" @@ -666,6 +672,7 @@ dependencies = [ "cssparser", "dashmap", "data-encoding", + "glob", "indoc", "itertools", "jemallocator", diff --git a/Cargo.toml b/Cargo.toml index 62d8ccb1..4a05c949 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ dashmap = { version = "5.0.0", optional = true } serde_json = { version = "1.0.78", optional = true } lightningcss-derive = { version = "1.0.0-alpha.37", path = "./derive", optional = true } schemars = { version = "0.8.11", features = ["smallvec"], optional = true } +glob = "0.3.1" [target.'cfg(target_os = "macos")'.dependencies] jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"], optional = true } diff --git a/src/main.rs b/src/main.rs index 363d3f08..43d1da0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use glob::glob; use atty::Stream; use clap::{ArgGroup, Parser}; use lightningcss::bundler::{Bundler, FileProvider}; @@ -66,26 +67,41 @@ struct SourceMapJson<'a> { names: &'a Vec, } +#[derive(Debug)] +struct InputFile { + filename: String, + source: String +} + pub fn main() -> Result<(), std::io::Error> { let cli_args = CliArgs::parse(); let project_root = std::env::current_dir()?; + + let mut files: Vec = Vec::new(); - // If we're given an input file, read from it and adjust its name. - // - // If we're not given an input file and stdin was redirected, read - // from it and create a fake name. Return an error if stdin was not - // redirected (otherwise the program will hang waiting for input). - // - let (filename, source) = match &cli_args.input_file { - Some(f) => { - let absolute_path = fs::canonicalize(f)?; - let filename = pathdiff::diff_paths(absolute_path, &project_root).unwrap(); - let filename = filename.to_string_lossy().into_owned(); - let contents = fs::read_to_string(f)?; - (filename, contents) - } + match &cli_args.input_file { + Some(g) => { + let globs = glob(g); + + match glob(g) { + Ok(file_paths) => { + for file in file_paths { + if let Ok(f) = file { + let absolute_path = fs::canonicalize(&f)?; + let filename = pathdiff::diff_paths(absolute_path, &project_root).unwrap(); + let filename = filename.to_string_lossy().into_owned(); + let contents = fs::read_to_string(&f)?; + files.push(InputFile { filename, source: contents }); + } + } + }, + Err(e) => { + eprintln!("{}", e); + std::process::exit(1); + } + } + }, None => { - // Don't silently wait for input if stdin was not redirected. if atty::is(Stream::Stdin) { return Err(io::Error::new( io::ErrorKind::Other, @@ -94,157 +110,164 @@ pub fn main() -> Result<(), std::io::Error> { } let filename = format!("stdin-{}", std::process::id()); let contents = io::read_to_string(io::stdin())?; - (filename, contents) + files.push(InputFile {filename, source: contents}); } - }; + } - let css_modules = if let Some(_) = cli_args.css_modules { - let pattern = if let Some(pattern) = cli_args.css_modules_pattern.as_ref() { - match lightningcss::css_modules::Pattern::parse(pattern) { - Ok(p) => p, - Err(e) => { - eprintln!("{}", e); - std::process::exit(1); + println!("{:#?}", &files); + + for file in files { + let InputFile { filename, source } = file; + println!("{}, {}", &filename, &source); + + let css_modules = if let Some(_) = cli_args.css_modules { + let pattern = if let Some(pattern) = cli_args.css_modules_pattern.as_ref() { + match lightningcss::css_modules::Pattern::parse(pattern) { + Ok(p) => p, + Err(e) => { + eprintln!("{}", e); + std::process::exit(1); + } } - } + } else { + Default::default() + }; + + Some(lightningcss::css_modules::Config { + pattern, + dashed_idents: cli_args.css_modules_dashed_idents, + ..Default::default() + }) } else { - Default::default() - }; - - Some(lightningcss::css_modules::Config { - pattern, - dashed_idents: cli_args.css_modules_dashed_idents, - ..Default::default() - }) - } else { - cli_args.css_modules.as_ref().map(|_| Default::default()) - }; - - let fs = FileProvider::new(); - let warnings = if cli_args.error_recovery { - Some(Arc::new(RwLock::new(Vec::new()))) - } else { - None - }; - - let mut source_map = if cli_args.sourcemap { - Some(SourceMap::new(&project_root.to_string_lossy())) - } else { - None - }; - - let res = { - let mut options = ParserOptions { - nesting: cli_args.nesting, - css_modules, - custom_media: cli_args.custom_media, - error_recovery: cli_args.error_recovery, - warnings: warnings.clone(), - ..ParserOptions::default() + cli_args.css_modules.as_ref().map(|_| Default::default()) }; - - let mut stylesheet = if cli_args.bundle { - let mut bundler = Bundler::new(&fs, source_map.as_mut(), options); - bundler.bundle(Path::new(&filename)).unwrap() + + let fs = FileProvider::new(); + let warnings = if cli_args.error_recovery { + Some(Arc::new(RwLock::new(Vec::new()))) } else { - if let Some(sm) = &mut source_map { - sm.add_source(&filename); - let _ = sm.set_source_content(0, &source); - } - options.filename = filename; - StyleSheet::parse(&source, options).unwrap() + None }; - - let targets = if !cli_args.targets.is_empty() { - Browsers::from_browserslist(cli_args.targets).unwrap() - } else if cli_args.browserslist { - Browsers::load_browserslist().unwrap() + + let mut source_map = if cli_args.sourcemap { + Some(SourceMap::new(&project_root.to_string_lossy())) } else { None }; - - stylesheet - .minify(MinifyOptions { - targets, - ..MinifyOptions::default() - }) - .unwrap(); - - stylesheet - .to_css(PrinterOptions { - minify: cli_args.minify, - source_map: source_map.as_mut(), - project_root: Some(&project_root.to_string_lossy()), - targets, - ..PrinterOptions::default() - }) - .unwrap() - }; - - let map = if let Some(ref mut source_map) = source_map { - let mut vlq_output: Vec = Vec::new(); - source_map - .write_vlq(&mut vlq_output) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "Error writing sourcemap vlq"))?; - - let sm = SourceMapJson { - version: 3, - mappings: unsafe { String::from_utf8_unchecked(vlq_output) }, - sources: source_map.get_sources(), - sources_content: source_map.get_sources_content(), - names: source_map.get_names(), + + let res = { + let mut options = ParserOptions { + nesting: cli_args.nesting, + css_modules, + custom_media: cli_args.custom_media, + error_recovery: cli_args.error_recovery, + warnings: warnings.clone(), + ..ParserOptions::default() + }; + + let mut stylesheet = if cli_args.bundle { + let mut bundler = Bundler::new(&fs, source_map.as_mut(), options); + bundler.bundle(Path::new(&filename)).unwrap() + } else { + if let Some(sm) = &mut source_map { + sm.add_source(&filename); + let _ = sm.set_source_content(0, &source); + } + options.filename = filename; + StyleSheet::parse(&source, options).unwrap() + }; + + let targets = if !cli_args.targets.is_empty() { + Browsers::from_browserslist(&cli_args.targets).unwrap() + } else if cli_args.browserslist { + Browsers::load_browserslist().unwrap() + } else { + None + }; + + stylesheet + .minify(MinifyOptions { + targets, + ..MinifyOptions::default() + }) + .unwrap(); + + stylesheet + .to_css(PrinterOptions { + minify: cli_args.minify, + source_map: source_map.as_mut(), + project_root: Some(&project_root.to_string_lossy()), + targets, + ..PrinterOptions::default() + }) + .unwrap() }; - - serde_json::to_vec(&sm).ok() - } else { - None - }; - - if let Some(warnings) = warnings { - let warnings = Arc::try_unwrap(warnings).unwrap().into_inner().unwrap(); - for warning in warnings { - eprintln!("{}", warning); - } - } - - if let Some(output_file) = &cli_args.output_file { - let mut code = res.code; - if cli_args.sourcemap { - if let Some(map_buf) = map { - let map_filename: String = output_file.to_owned() + ".map"; - code += &format!("\n/*# sourceMappingURL={} */\n", map_filename); - fs::write(map_filename, map_buf)?; + + let map = if let Some(ref mut source_map) = source_map { + let mut vlq_output: Vec = Vec::new(); + source_map + .write_vlq(&mut vlq_output) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "Error writing sourcemap vlq"))?; + + let sm = SourceMapJson { + version: 3, + mappings: unsafe { String::from_utf8_unchecked(vlq_output) }, + sources: source_map.get_sources(), + sources_content: source_map.get_sources_content(), + names: source_map.get_names(), + }; + + serde_json::to_vec(&sm).ok() + } else { + None + }; + + if let Some(warnings) = warnings { + let warnings = Arc::try_unwrap(warnings).unwrap().into_inner().unwrap(); + for warning in warnings { + eprintln!("{}", warning); } } - - let output_path = Path::new(output_file); - if let Some(p) = output_path.parent() { - fs::create_dir_all(p)? - }; - fs::write(output_file, code.as_bytes())?; - - if let Some(css_modules) = cli_args.css_modules { - let css_modules_filename = if let Some(name) = css_modules { - name - } else { - infer_css_modules_filename(&output_file)? + + if let Some(output_file) = &cli_args.output_file { + let mut code = res.code; + if cli_args.sourcemap { + if let Some(map_buf) = map { + let map_filename: String = output_file.to_owned() + ".map"; + code += &format!("\n/*# sourceMappingURL={} */\n", map_filename); + fs::write(map_filename, map_buf)?; + } + } + + let output_path = Path::new(output_file); + if let Some(p) = output_path.parent() { + fs::create_dir_all(p)? }; - if let Some(exports) = res.exports { - let css_modules_json = serde_json::to_string(&exports)?; - fs::write(css_modules_filename, css_modules_json)?; + fs::write(output_file, code.as_bytes())?; + + if let Some(css_modules) = &cli_args.css_modules { + let css_modules_filename = if let Some(name) = css_modules { + name + } else { + infer_css_modules_filename(&output_file)? + }; + if let Some(exports) = res.exports { + let css_modules_json = serde_json::to_string(&exports)?; + fs::write(css_modules_filename, css_modules_json)?; + } } - } - } else { - if let Some(exports) = res.exports { - println!( - "{}", - serde_json::json!({ - "code": res.code, - "exports": exports - }) - ); } else { - println!("{}", res.code); + if let Some(exports) = res.exports { + println!( + "{}", + serde_json::json!({ + "code": res.code, + "exports": exports + }) + ); + } else { + println!("{}", res.code); + } } } diff --git a/test.css b/test.css new file mode 100644 index 00000000..41e667f9 --- /dev/null +++ b/test.css @@ -0,0 +1,3 @@ +html { + color:red; +} \ No newline at end of file diff --git a/test2.css b/test2.css new file mode 100644 index 00000000..e69de29b From 2037e4c2ebf614a30e63f94da78d23b239c12eeb Mon Sep 17 00:00:00 2001 From: thepassle Date: Wed, 22 Feb 2023 10:27:42 +0100 Subject: [PATCH 2/3] fix: if else incompatible type --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 43d1da0e..7aa2dcea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -247,7 +247,7 @@ pub fn main() -> Result<(), std::io::Error> { if let Some(css_modules) = &cli_args.css_modules { let css_modules_filename = if let Some(name) = css_modules { - name + name.to_string() } else { infer_css_modules_filename(&output_file)? }; From 531d722f3e374f3b7199ad577ae110154a082b21 Mon Sep 17 00:00:00 2001 From: thepassle Date: Wed, 22 Feb 2023 10:30:39 +0100 Subject: [PATCH 3/3] chore: cleanup --- src/main.rs | 6 ------ test2.css | 3 +++ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7aa2dcea..64efc96f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,7 +67,6 @@ struct SourceMapJson<'a> { names: &'a Vec, } -#[derive(Debug)] struct InputFile { filename: String, source: String @@ -81,8 +80,6 @@ pub fn main() -> Result<(), std::io::Error> { match &cli_args.input_file { Some(g) => { - let globs = glob(g); - match glob(g) { Ok(file_paths) => { for file in file_paths { @@ -113,12 +110,9 @@ pub fn main() -> Result<(), std::io::Error> { files.push(InputFile {filename, source: contents}); } } - - println!("{:#?}", &files); for file in files { let InputFile { filename, source } = file; - println!("{}, {}", &filename, &source); let css_modules = if let Some(_) = cli_args.css_modules { let pattern = if let Some(pattern) = cli_args.css_modules_pattern.as_ref() { diff --git a/test2.css b/test2.css index e69de29b..ac3f1685 100644 --- a/test2.css +++ b/test2.css @@ -0,0 +1,3 @@ +body { + background: green; +} \ No newline at end of file