Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include minify_css and minify_js in the public API #67

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 68 additions & 20 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,51 @@
use std::fmt;
use std::fs::File;
use std::io::{stdin, stdout, Read, Write};
use std::str::FromStr;

use structopt::StructOpt;

use minify_html::{minify, Cfg};
use minify_html::{minify, minify_css, minify_js, Cfg};

#[derive(PartialEq)]
enum SourceType {
Html,
Css,
Js,
}

impl Default for SourceType {
fn default() -> Self {
SourceType::Html
}
}

impl fmt::Display for SourceType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
SourceType::Html => "html",
SourceType::Css => "css",
SourceType::Js => "js",
}
)
}
}

impl FromStr for SourceType {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"html" => Ok(SourceType::Html),
"css" => Ok(SourceType::Css),
"js" => Ok(SourceType::Js),
_ => Err(format!("\"{}\" is not \"html\", \"css\" or \"js\".", s)),
}
}
}

#[derive(StructOpt)]
#[structopt(
Expand All @@ -20,11 +62,11 @@ struct Cli {
#[structopt(short, long, parse(from_os_str))]
output: Option<std::path::PathBuf>,

/// Minify JS in `<script>` tags that have a valid or no `type` attribute value.
/// Minify Js in `<script>` tags that have a valid or no `type` attribute value.
#[structopt(long)]
minify_js: bool,

/// Minify CSS in `<style>` tags and `style` attributes.
/// Minify Css in `<style>` tags and `style` attributes.
#[structopt(long)]
minify_css: bool,

Expand All @@ -44,7 +86,7 @@ struct Cli {
#[structopt(long)]
keep_html_and_head_opening_tags: bool,

/// Keep spaces between attributes when possible to conform to HTML standards.
/// Keep spaces between attributes when possible to conform to Html standards.
#[structopt(long)]
keep_spaces_between_attributes: bool,

Expand All @@ -59,6 +101,10 @@ struct Cli {
/// Remove all processing_instructions.
#[structopt(long)]
remove_processing_instructions: bool,

/// Allowed values: "html", "css" or "js" (not case-sensitive).
#[structopt(default_value, long)]
source_type: SourceType,
}

macro_rules! io_expect {
Expand All @@ -85,22 +131,24 @@ fn main() {
src_file.read_to_end(&mut src_code),
"could not load source code"
);
let out_code = minify(
&src_code,
&Cfg {
do_not_minify_doctype: args.do_not_minify_doctype,
ensure_spec_compliant_unquoted_attribute_values: args
.ensure_spec_compliant_unquoted_attribute_values,
keep_closing_tags: args.keep_closing_tags,
keep_comments: args.keep_comments,
keep_html_and_head_opening_tags: args.keep_html_and_head_opening_tags,
keep_spaces_between_attributes: args.keep_spaces_between_attributes,
minify_css: args.minify_css,
minify_js: args.minify_js,
remove_bangs: args.remove_bangs,
remove_processing_instructions: args.remove_processing_instructions,
},
);
let cfg = &Cfg {
do_not_minify_doctype: args.do_not_minify_doctype,
ensure_spec_compliant_unquoted_attribute_values: args
.ensure_spec_compliant_unquoted_attribute_values,
keep_closing_tags: args.keep_closing_tags,
keep_comments: args.keep_comments,
keep_html_and_head_opening_tags: args.keep_html_and_head_opening_tags,
keep_spaces_between_attributes: args.keep_spaces_between_attributes,
minify_css: args.minify_css | (args.source_type == SourceType::Css),
minify_js: args.minify_js | (args.source_type == SourceType::Js),
remove_bangs: args.remove_bangs,
remove_processing_instructions: args.remove_processing_instructions,
};
let out_code = match args.source_type {
SourceType::Html => minify(&src_code, cfg),
SourceType::Css => minify_css(&src_code, cfg),
SourceType::Js => minify_js(&src_code, cfg),
};
let mut out_file: Box<dyn Write> = match args.output {
Some(p) => Box::new(io_expect!(File::create(p), "could not open output file")),
None => Box::new(stdout()),
Expand Down
3 changes: 3 additions & 0 deletions python/Cargo.core.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ edition = "2018"
name = "minify_html_core"
crate-type = ["cdylib"]

[features]
js-esbuild = []

[dependencies]
minify-html = { path = "../rust/main", features = [] }
[dependencies.pyo3]
Expand Down
4 changes: 4 additions & 0 deletions python/Cargo.js.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ edition = "2018"
name = "minify_html"
crate-type = ["cdylib"]

[features]
default = [ "js-esbuild" ]
js-esbuild = []

[dependencies]
minify-html = { path = "../rust/main", features = ["js-esbuild"] }
[dependencies.pyo3]
Expand Down
84 changes: 84 additions & 0 deletions python/src/lib.template.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use minify_html::{Cfg, minify as minify_html_native};
#[cfg(feature = "js-esbuild")]
use minify_html::{minify_css as minify_css_native, minify_js as minify_js_native};
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use std::string::String;
Expand Down Expand Up @@ -45,9 +47,91 @@ fn minify(
Ok(String::from_utf8(out_code).unwrap())
}

#[cfg(feature = "js-esbuild")]
#[pyfunction(
py_args = "*",
do_not_minify_doctype = "false",
ensure_spec_compliant_unquoted_attribute_values = "false",
keep_closing_tags = "false",
keep_comments = "false",
keep_html_and_head_opening_tags = "false",
keep_spaces_between_attributes = "false",
remove_bangs = "false",
remove_processing_instructions = "false",
)]
fn minify_css(
code: String,
do_not_minify_doctype: bool,
ensure_spec_compliant_unquoted_attribute_values: bool,
keep_closing_tags: bool,
keep_comments: bool,
keep_html_and_head_opening_tags: bool,
keep_spaces_between_attributes: bool,
remove_bangs: bool,
remove_processing_instructions: bool,
) -> PyResult<String> {
let code = code.into_bytes();
let out_code = minify_css_native(&code, &Cfg {
do_not_minify_doctype,
ensure_spec_compliant_unquoted_attribute_values,
keep_closing_tags,
keep_comments,
keep_html_and_head_opening_tags,
keep_spaces_between_attributes,
minify_css: true,
minify_js: false,
remove_bangs,
remove_processing_instructions,
});
Ok(String::from_utf8(out_code).unwrap())
}

#[cfg(feature = "js-esbuild")]
#[pyfunction(
py_args = "*",
do_not_minify_doctype = "false",
ensure_spec_compliant_unquoted_attribute_values = "false",
keep_closing_tags = "false",
keep_comments = "false",
keep_html_and_head_opening_tags = "false",
keep_spaces_between_attributes = "false",
remove_bangs = "false",
remove_processing_instructions = "false",
)]
fn minify_js(
code: String,
do_not_minify_doctype: bool,
ensure_spec_compliant_unquoted_attribute_values: bool,
keep_closing_tags: bool,
keep_comments: bool,
keep_html_and_head_opening_tags: bool,
keep_spaces_between_attributes: bool,
remove_bangs: bool,
remove_processing_instructions: bool,
) -> PyResult<String> {
let code = code.into_bytes();
let out_code = minify_js_native(&code, &Cfg {
do_not_minify_doctype,
ensure_spec_compliant_unquoted_attribute_values,
keep_closing_tags,
keep_comments,
keep_html_and_head_opening_tags,
keep_spaces_between_attributes,
minify_css: false,
minify_js: true,
remove_bangs,
remove_processing_instructions,
});
Ok(String::from_utf8(out_code).unwrap())
}

#[pymodule]
fn REPLACE_WITH_MODULE_NAME(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(minify))?;
#[cfg(feature = "js-esbuild")]
m.add_wrapped(wrap_pyfunction!(minify_css))?;
#[cfg(feature = "js-esbuild")]
m.add_wrapped(wrap_pyfunction!(minify_js))?;

Ok(())
}
58 changes: 58 additions & 0 deletions rust/main/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub use crate::cfg::Cfg;
use crate::common::spec::tag::ns::Namespace;
use crate::common::spec::tag::EMPTY_SLICE;
use crate::minify::content::minify_content;
use crate::minify::css::minify_css as minify_css_internal;
use crate::minify::js::minify_js as minify_js_internal;
use crate::parse::content::parse_content;
use crate::parse::Code;

Expand Down Expand Up @@ -50,6 +52,62 @@ pub fn minify(src: &[u8], cfg: &Cfg) -> Vec<u8> {
out
}

/// Minifies UTF-8 CSS code, represented as an array of bytes.
///
/// [esbuild-rs](https://github.com/wilsonzlin/esbuild-rs) is used to minify
/// the CSS. The `js-esbuild` feature must be enabled, and cfg.minify_css must
/// be `true`; otherwise, this function has no effect.
///
/// # Arguments
///
/// * `code` - A slice of bytes representing the source code to minify.
/// * `cfg` - Configuration object to adjust minification approach.
///
/// # Examples
///
/// ```
/// use minify_html::{Cfg, minify_css};
///
/// let code: &[u8] = b"a { color: red; }";
/// let mut cfg = Cfg::new();
/// cfg.minify_css = true;
/// let minified = minify_css(&code, &cfg);
/// assert!(minified.len() < code.len());
/// ```
pub fn minify_css(src: &[u8], cfg: &Cfg) -> Vec<u8> {
let mut out = Vec::with_capacity(src.len());
minify_css_internal(cfg, &mut out, src);
out
}

/// Minifies UTF-8 JavaScript code, represented as an array of bytes.
///
/// [esbuild-rs](https://github.com/wilsonzlin/esbuild-rs) is used to minify
/// the JS. The `js-esbuild` feature must be enabled, and cfg.minify_js must
/// be `true`; otherwise, this function has no effect.
///
/// # Arguments
///
/// * `code` - A slice of bytes representing the source code to minify.
/// * `cfg` - Configuration object to adjust minification approach.
///
/// # Examples
///
/// ```
/// use minify_html::{Cfg, minify_js};
///
/// let code: &[u8] = b"alert( 'Hello, world!' );";
/// let mut cfg = Cfg::new();
/// cfg.minify_js = true;
/// let minified = minify_js(&code, &cfg);
/// assert!(minified.len() < code.len());
/// ```
pub fn minify_js(src: &[u8], cfg: &Cfg) -> Vec<u8> {
let mut out = Vec::with_capacity(src.len());
minify_js_internal(cfg, &mut out, src);
out
}

pub fn canonicalise<T: Write>(out: &mut T, src: &[u8]) -> std::io::Result<()> {
let mut code = Code::new(src);
let parsed = parse_content(&mut code, Namespace::Html, EMPTY_SLICE, EMPTY_SLICE);
Expand Down