Implement parallel processing with CLI
This commit is contained in:
parent
7263c7a95d
commit
a6035d6f0f
|
@ -5,6 +5,7 @@
|
||||||
- Fix Node.js dependency version.
|
- Fix Node.js dependency version.
|
||||||
- Create onepass variant for Python.
|
- Create onepass variant for Python.
|
||||||
- Bump [minify-js](https://github.com/wilsonzlin/minify-js) to 0.1.1.
|
- Bump [minify-js](https://github.com/wilsonzlin/minify-js) to 0.1.1.
|
||||||
|
- Implement parallel in-place minification for CLI.
|
||||||
|
|
||||||
## 0.9.1
|
## 0.9.1
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,12 @@ Use the `--help` argument for more details.
|
||||||
minify-html --output /path/to/output.min.html --keep-closing-tags --minify-css /path/to/src.html
|
minify-html --output /path/to/output.min.html --keep-closing-tags --minify-css /path/to/src.html
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To quickly parallel process a batch of files in place:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minify-html --keep-closing-tags --minify-css /path/to/**/*.html
|
||||||
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
|
@ -8,4 +8,6 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
minify-html = { path = "../rust/main" }
|
minify-html = { path = "../rust/main" }
|
||||||
|
num_cpus = "1.13.1"
|
||||||
|
spmc = "0.3.0"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
|
|
137
cli/src/main.rs
137
cli/src/main.rs
|
@ -1,5 +1,9 @@
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, stdout, Read, Write};
|
use std::io::{stdin, stdout, Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::exit;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
@ -12,9 +16,9 @@ use minify_html::{minify, Cfg};
|
||||||
)]
|
)]
|
||||||
// WARNING: Keep descriptions in sync with Cfg.
|
// WARNING: Keep descriptions in sync with Cfg.
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// File to minify; omit for stdin.
|
/// Files to minify; omit for stdin. If more than one is provided, they will be parallel minified in place, and --output must be omitted.
|
||||||
#[structopt(parse(from_os_str))]
|
#[structopt(parse(from_os_str))]
|
||||||
input: Option<std::path::PathBuf>,
|
inputs: Vec<std::path::PathBuf>,
|
||||||
|
|
||||||
/// Output destination; omit for stdout.
|
/// Output destination; omit for stdout.
|
||||||
#[structopt(short, long, parse(from_os_str))]
|
#[structopt(short, long, parse(from_os_str))]
|
||||||
|
@ -62,12 +66,11 @@ struct Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! io_expect {
|
macro_rules! io_expect {
|
||||||
($expr:expr, $msg:literal) => {
|
($name:expr, $expr:expr, $msg:literal) => {
|
||||||
match $expr {
|
match $expr {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error: {}", $msg);
|
eprintln!("[{}] {}: {}", $name, $msg, e);
|
||||||
eprintln!("{}", e);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,37 +79,97 @@ macro_rules! io_expect {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = Cli::from_args();
|
let args = Cli::from_args();
|
||||||
let mut src_code = Vec::<u8>::new();
|
if args.output.is_some() && args.inputs.len() > 1 {
|
||||||
let mut src_file: Box<dyn Read> = match args.input {
|
eprintln!("Cannot have --output when multiple inputs are provided.");
|
||||||
Some(p) => Box::new(io_expect!(File::open(p), "could not open source file")),
|
exit(1);
|
||||||
None => Box::new(stdin()),
|
|
||||||
};
|
};
|
||||||
io_expect!(
|
|
||||||
src_file.read_to_end(&mut src_code),
|
let cfg = Arc::new(Cfg {
|
||||||
"could not load source code"
|
do_not_minify_doctype: args.do_not_minify_doctype,
|
||||||
);
|
ensure_spec_compliant_unquoted_attribute_values: args
|
||||||
let out_code = minify(
|
.ensure_spec_compliant_unquoted_attribute_values,
|
||||||
&src_code,
|
keep_closing_tags: args.keep_closing_tags,
|
||||||
&Cfg {
|
keep_comments: args.keep_comments,
|
||||||
do_not_minify_doctype: args.do_not_minify_doctype,
|
keep_html_and_head_opening_tags: args.keep_html_and_head_opening_tags,
|
||||||
ensure_spec_compliant_unquoted_attribute_values: args
|
keep_spaces_between_attributes: args.keep_spaces_between_attributes,
|
||||||
.ensure_spec_compliant_unquoted_attribute_values,
|
minify_css: args.minify_css,
|
||||||
keep_closing_tags: args.keep_closing_tags,
|
minify_js: args.minify_js,
|
||||||
keep_comments: args.keep_comments,
|
remove_bangs: args.remove_bangs,
|
||||||
keep_html_and_head_opening_tags: args.keep_html_and_head_opening_tags,
|
remove_processing_instructions: args.remove_processing_instructions,
|
||||||
keep_spaces_between_attributes: args.keep_spaces_between_attributes,
|
});
|
||||||
minify_css: args.minify_css,
|
|
||||||
minify_js: args.minify_js,
|
if args.inputs.len() < 1 {
|
||||||
remove_bangs: args.remove_bangs,
|
let input_name = args
|
||||||
remove_processing_instructions: args.remove_processing_instructions,
|
.inputs
|
||||||
},
|
.get(0)
|
||||||
);
|
.map(|p| p.to_string_lossy().into_owned())
|
||||||
let mut out_file: Box<dyn Write> = match args.output {
|
.unwrap_or("stdin".to_string());
|
||||||
Some(p) => Box::new(io_expect!(File::create(p), "could not open output file")),
|
let mut src_file: Box<dyn Read> = match args.inputs.get(0) {
|
||||||
None => Box::new(stdout()),
|
Some(p) => Box::new(io_expect!(
|
||||||
};
|
input_name,
|
||||||
io_expect!(
|
File::open(p),
|
||||||
out_file.write_all(&out_code),
|
"could not open source file"
|
||||||
"could not save minified code"
|
)),
|
||||||
);
|
None => Box::new(stdin()),
|
||||||
|
};
|
||||||
|
let mut src_code = Vec::<u8>::new();
|
||||||
|
io_expect!(
|
||||||
|
input_name,
|
||||||
|
src_file.read_to_end(&mut src_code),
|
||||||
|
"could not load source code"
|
||||||
|
);
|
||||||
|
let out_code = minify(&src_code, &cfg);
|
||||||
|
let mut out_file: Box<dyn Write> = match args.output {
|
||||||
|
Some(p) => Box::new(io_expect!(
|
||||||
|
input_name,
|
||||||
|
File::create(p),
|
||||||
|
"could not open output file"
|
||||||
|
)),
|
||||||
|
None => Box::new(stdout()),
|
||||||
|
};
|
||||||
|
io_expect!(
|
||||||
|
input_name,
|
||||||
|
out_file.write_all(&out_code),
|
||||||
|
"could not save minified code"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let (mut tx, rx): (spmc::Sender<PathBuf>, spmc::Receiver<PathBuf>) = spmc::channel();
|
||||||
|
let mut handles = Vec::new();
|
||||||
|
for _ in 0..num_cpus::get() {
|
||||||
|
let rx = rx.clone();
|
||||||
|
let cfg = cfg.clone();
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
let input = rx.recv().unwrap();
|
||||||
|
let input_name = input.to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
let mut src_file =
|
||||||
|
io_expect!(input_name, File::open(&input), "could not open source file");
|
||||||
|
let mut src_code = Vec::<u8>::new();
|
||||||
|
io_expect!(
|
||||||
|
input_name,
|
||||||
|
src_file.read_to_end(&mut src_code),
|
||||||
|
"could not load source code"
|
||||||
|
);
|
||||||
|
let out_code = minify(&src_code, &cfg);
|
||||||
|
let mut out_file = io_expect!(
|
||||||
|
input_name,
|
||||||
|
File::create(&input),
|
||||||
|
"could not open output file"
|
||||||
|
);
|
||||||
|
io_expect!(
|
||||||
|
input_name,
|
||||||
|
out_file.write_all(&out_code),
|
||||||
|
"could not save minified code"
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in args.inputs {
|
||||||
|
tx.send(i).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue