Change default CSS minifier optimising level and allow configuring it
This commit is contained in:
parent
d4e0887465
commit
ad21466d2c
|
@ -1,5 +1,10 @@
|
||||||
# minify-html changelog
|
# minify-html changelog
|
||||||
|
|
||||||
|
## Pending
|
||||||
|
|
||||||
|
- Change the default CSS minifier optimisation level to 1, as higher levels may perform dangerous optimisations.
|
||||||
|
- Allow configuring the CSS minifier optimisation level.
|
||||||
|
|
||||||
## 0.10.8
|
## 0.10.8
|
||||||
|
|
||||||
- [Node.js] Fix assertion failure panic on invalid argument type.
|
- [Node.js] Fix assertion failure panic on invalid argument type.
|
||||||
|
|
|
@ -31,6 +31,18 @@ struct Cli {
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
minify_css: bool,
|
minify_css: bool,
|
||||||
|
|
||||||
|
/// Use optimisation level 1 for the CSS minifier.
|
||||||
|
#[structopt(long)]
|
||||||
|
minify_css_level_1: bool,
|
||||||
|
|
||||||
|
/// Use optimisation level 2 for the CSS minifier. May perform some dangerous optimisations.
|
||||||
|
#[structopt(long)]
|
||||||
|
minify_css_level_2: bool,
|
||||||
|
|
||||||
|
/// Use optimisation level 3 for the CSS minifier. May perform many dangerous optimisations.
|
||||||
|
#[structopt(long)]
|
||||||
|
minify_css_level_3: bool,
|
||||||
|
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
/// Do not minify DOCTYPEs. Minified DOCTYPEs may not be spec compliant.
|
/// Do not minify DOCTYPEs. Minified DOCTYPEs may not be spec compliant.
|
||||||
do_not_minify_doctype: bool,
|
do_not_minify_doctype: bool,
|
||||||
|
@ -92,6 +104,9 @@ fn main() {
|
||||||
keep_html_and_head_opening_tags: args.keep_html_and_head_opening_tags,
|
keep_html_and_head_opening_tags: args.keep_html_and_head_opening_tags,
|
||||||
keep_spaces_between_attributes: args.keep_spaces_between_attributes,
|
keep_spaces_between_attributes: args.keep_spaces_between_attributes,
|
||||||
minify_css: args.minify_css,
|
minify_css: args.minify_css,
|
||||||
|
minify_css_level_1: args.minify_css_level_1,
|
||||||
|
minify_css_level_2: args.minify_css_level_2,
|
||||||
|
minify_css_level_3: args.minify_css_level_3,
|
||||||
minify_js: args.minify_js,
|
minify_js: args.minify_js,
|
||||||
remove_bangs: args.remove_bangs,
|
remove_bangs: args.remove_bangs,
|
||||||
remove_processing_instructions: args.remove_processing_instructions,
|
remove_processing_instructions: args.remove_processing_instructions,
|
||||||
|
|
|
@ -11,6 +11,9 @@ public class Configuration {
|
||||||
public final boolean keep_html_and_head_opening_tags;
|
public final boolean keep_html_and_head_opening_tags;
|
||||||
public final boolean keep_spaces_between_attributes;
|
public final boolean keep_spaces_between_attributes;
|
||||||
public final boolean minify_css;
|
public final boolean minify_css;
|
||||||
|
public final boolean minify_css_level_1;
|
||||||
|
public final boolean minify_css_level_2;
|
||||||
|
public final boolean minify_css_level_3;
|
||||||
public final boolean minify_js;
|
public final boolean minify_js;
|
||||||
public final boolean remove_bangs;
|
public final boolean remove_bangs;
|
||||||
public final boolean remove_processing_instructions;
|
public final boolean remove_processing_instructions;
|
||||||
|
@ -23,6 +26,9 @@ public class Configuration {
|
||||||
boolean keep_html_and_head_opening_tags,
|
boolean keep_html_and_head_opening_tags,
|
||||||
boolean keep_spaces_between_attributes,
|
boolean keep_spaces_between_attributes,
|
||||||
boolean minify_css,
|
boolean minify_css,
|
||||||
|
boolean minify_css_level_1,
|
||||||
|
boolean minify_css_level_2,
|
||||||
|
boolean minify_css_level_3,
|
||||||
boolean minify_js,
|
boolean minify_js,
|
||||||
boolean remove_bangs,
|
boolean remove_bangs,
|
||||||
boolean remove_processing_instructions
|
boolean remove_processing_instructions
|
||||||
|
@ -34,6 +40,9 @@ public class Configuration {
|
||||||
this.keep_html_and_head_opening_tags = keep_html_and_head_opening_tags;
|
this.keep_html_and_head_opening_tags = keep_html_and_head_opening_tags;
|
||||||
this.keep_spaces_between_attributes = keep_spaces_between_attributes;
|
this.keep_spaces_between_attributes = keep_spaces_between_attributes;
|
||||||
this.minify_css = minify_css;
|
this.minify_css = minify_css;
|
||||||
|
this.minify_css_level_1 = minify_css_level_1;
|
||||||
|
this.minify_css_level_2 = minify_css_level_2;
|
||||||
|
this.minify_css_level_3 = minify_css_level_3;
|
||||||
this.minify_js = minify_js;
|
this.minify_js = minify_js;
|
||||||
this.remove_bangs = remove_bangs;
|
this.remove_bangs = remove_bangs;
|
||||||
this.remove_processing_instructions = remove_processing_instructions;
|
this.remove_processing_instructions = remove_processing_instructions;
|
||||||
|
@ -50,6 +59,9 @@ public class Configuration {
|
||||||
private boolean keep_html_and_head_opening_tags = false;
|
private boolean keep_html_and_head_opening_tags = false;
|
||||||
private boolean keep_spaces_between_attributes = false;
|
private boolean keep_spaces_between_attributes = false;
|
||||||
private boolean minify_css = false;
|
private boolean minify_css = false;
|
||||||
|
private boolean minify_css_level_1 = false;
|
||||||
|
private boolean minify_css_level_2 = false;
|
||||||
|
private boolean minify_css_level_3 = false;
|
||||||
private boolean minify_js = false;
|
private boolean minify_js = false;
|
||||||
private boolean remove_bangs = false;
|
private boolean remove_bangs = false;
|
||||||
private boolean remove_processing_instructions = false;
|
private boolean remove_processing_instructions = false;
|
||||||
|
@ -89,6 +101,21 @@ public class Configuration {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setMinifyCssLevel1(boolean val) {
|
||||||
|
this.minify_css_level_1 = val;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setMinifyCssLevel2(boolean val) {
|
||||||
|
this.minify_css_level_2 = val;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setMinifyCssLevel3(boolean val) {
|
||||||
|
this.minify_css_level_3 = val;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setMinifyJs(boolean val) {
|
public Builder setMinifyJs(boolean val) {
|
||||||
this.minify_js = val;
|
this.minify_js = val;
|
||||||
return this;
|
return this;
|
||||||
|
@ -114,6 +141,9 @@ public class Configuration {
|
||||||
this.keep_html_and_head_opening_tags,
|
this.keep_html_and_head_opening_tags,
|
||||||
this.keep_spaces_between_attributes,
|
this.keep_spaces_between_attributes,
|
||||||
this.minify_css,
|
this.minify_css,
|
||||||
|
this.minify_css_level_1,
|
||||||
|
this.minify_css_level_2,
|
||||||
|
this.minify_css_level_3,
|
||||||
this.minify_js,
|
this.minify_js,
|
||||||
this.remove_bangs,
|
this.remove_bangs,
|
||||||
this.remove_processing_instructions
|
this.remove_processing_instructions
|
||||||
|
|
|
@ -28,6 +28,18 @@ export function minify(
|
||||||
* If enabled, CSS in `<style>` tags and `style` attributes will be minified.
|
* If enabled, CSS in `<style>` tags and `style` attributes will be minified.
|
||||||
*/
|
*/
|
||||||
minify_css?: boolean;
|
minify_css?: boolean;
|
||||||
|
/**
|
||||||
|
* Use optimisation level 1 for the CSS minifier. This is currently the default, but may change in the future if higher levels become safe.
|
||||||
|
*/
|
||||||
|
minify_css_level_1?: boolean;
|
||||||
|
/**
|
||||||
|
* Use optimisation level 2 for the CSS minifier. This is mostly safe, but may perform some dangerous optimisations.
|
||||||
|
*/
|
||||||
|
minify_css_level_2?: boolean;
|
||||||
|
/**
|
||||||
|
* Use optimisation level 3 for the CSS minifier. This performs many dangerous optimisations, so ensure any input works with this level.
|
||||||
|
*/
|
||||||
|
minify_css_level_3?: boolean;
|
||||||
/** Remove all bangs. */
|
/** Remove all bangs. */
|
||||||
remove_bangs?: boolean;
|
remove_bangs?: boolean;
|
||||||
/** Remove all processing_instructions. */
|
/** Remove all processing_instructions. */
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
use neon::prelude::*;
|
use neon::prelude::*;
|
||||||
use neon::types::buffer::TypedArray;
|
use neon::types::buffer::TypedArray;
|
||||||
|
|
||||||
|
macro_rules! get_bool {
|
||||||
|
($cx:expr, $opt:expr, $name:literal) => {
|
||||||
|
$opt.get_opt::<JsBoolean, _, _>(&mut $cx, $name)?
|
||||||
|
.map(|v| v.value(&mut $cx))
|
||||||
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn minify(mut cx: FunctionContext) -> JsResult<JsBuffer> {
|
fn minify(mut cx: FunctionContext) -> JsResult<JsBuffer> {
|
||||||
let Ok(src) = cx.try_catch(|cx| cx.argument::<JsBuffer>(0)) else {
|
let Ok(src) = cx.try_catch(|cx| cx.argument::<JsBuffer>(0)) else {
|
||||||
return cx.throw_type_error("the first argument is not a Buffer");
|
return cx.throw_type_error("the first argument is not a Buffer");
|
||||||
|
@ -9,46 +17,23 @@ fn minify(mut cx: FunctionContext) -> JsResult<JsBuffer> {
|
||||||
return cx.throw_type_error("the second argument is not an object");
|
return cx.throw_type_error("the second argument is not an object");
|
||||||
};
|
};
|
||||||
let cfg = minify_html::Cfg {
|
let cfg = minify_html::Cfg {
|
||||||
do_not_minify_doctype: opt
|
do_not_minify_doctype: get_bool!(cx, opt, "do_not_minify_doctype"),
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "do_not_minify_doctype")?
|
ensure_spec_compliant_unquoted_attribute_values: get_bool!(
|
||||||
.map(|v| v.value(&mut cx))
|
cx,
|
||||||
.unwrap_or(false),
|
opt,
|
||||||
ensure_spec_compliant_unquoted_attribute_values: opt
|
"ensure_spec_compliant_unquoted_attribute_values"
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "ensure_spec_compliant_unquoted_attribute_values")?
|
),
|
||||||
.map(|v| v.value(&mut cx))
|
keep_closing_tags: get_bool!(cx, opt, "keep_closing_tags"),
|
||||||
.unwrap_or(false),
|
keep_html_and_head_opening_tags: get_bool!(cx, opt, "keep_html_and_head_opening_tags"),
|
||||||
keep_closing_tags: opt
|
keep_spaces_between_attributes: get_bool!(cx, opt, "keep_spaces_between_attributes"),
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "keep_closing_tags")?
|
keep_comments: get_bool!(cx, opt, "keep_comments"),
|
||||||
.map(|v| v.value(&mut cx))
|
minify_css: get_bool!(cx, opt, "minify_css"),
|
||||||
.unwrap_or(false),
|
minify_css_level_1: get_bool!(cx, opt, "minify_css_level_1"),
|
||||||
keep_html_and_head_opening_tags: opt
|
minify_css_level_2: get_bool!(cx, opt, "minify_css_level_2"),
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "keep_html_and_head_opening_tags")?
|
minify_css_level_3: get_bool!(cx, opt, "minify_css_level_3"),
|
||||||
.map(|v| v.value(&mut cx))
|
minify_js: get_bool!(cx, opt, "minify_js"),
|
||||||
.unwrap_or(false),
|
remove_bangs: get_bool!(cx, opt, "remove_bangs"),
|
||||||
keep_spaces_between_attributes: opt
|
remove_processing_instructions: get_bool!(cx, opt, "remove_processing_instructions"),
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "keep_spaces_between_attributes")?
|
|
||||||
.map(|v| v.value(&mut cx))
|
|
||||||
.unwrap_or(false),
|
|
||||||
keep_comments: opt
|
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "keep_comments")?
|
|
||||||
.map(|v| v.value(&mut cx))
|
|
||||||
.unwrap_or(false),
|
|
||||||
minify_css: opt
|
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "minify_css")?
|
|
||||||
.map(|v| v.value(&mut cx))
|
|
||||||
.unwrap_or(false),
|
|
||||||
minify_js: opt
|
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "minify_js")?
|
|
||||||
.map(|v| v.value(&mut cx))
|
|
||||||
.unwrap_or(false),
|
|
||||||
remove_bangs: opt
|
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "remove_bangs")?
|
|
||||||
.map(|v| v.value(&mut cx))
|
|
||||||
.unwrap_or(false),
|
|
||||||
remove_processing_instructions: opt
|
|
||||||
.get_opt::<JsBoolean, _, _>(&mut cx, "remove_processing_instructions")?
|
|
||||||
.map(|v| v.value(&mut cx))
|
|
||||||
.unwrap_or(false),
|
|
||||||
};
|
};
|
||||||
let out = minify_html::minify(src.as_slice(&mut cx), &cfg);
|
let out = minify_html::minify(src.as_slice(&mut cx), &cfg);
|
||||||
Ok(JsBuffer::external(&mut cx, out))
|
Ok(JsBuffer::external(&mut cx, out))
|
||||||
|
|
|
@ -25,6 +25,9 @@ fn minify(
|
||||||
keep_html_and_head_opening_tags: bool,
|
keep_html_and_head_opening_tags: bool,
|
||||||
keep_spaces_between_attributes: bool,
|
keep_spaces_between_attributes: bool,
|
||||||
minify_css: bool,
|
minify_css: bool,
|
||||||
|
minify_css_level_1: bool,
|
||||||
|
minify_css_level_2: bool,
|
||||||
|
minify_css_level_3: bool,
|
||||||
minify_js: bool,
|
minify_js: bool,
|
||||||
remove_bangs: bool,
|
remove_bangs: bool,
|
||||||
remove_processing_instructions: bool,
|
remove_processing_instructions: bool,
|
||||||
|
@ -40,6 +43,9 @@ fn minify(
|
||||||
keep_html_and_head_opening_tags,
|
keep_html_and_head_opening_tags,
|
||||||
keep_spaces_between_attributes,
|
keep_spaces_between_attributes,
|
||||||
minify_css,
|
minify_css,
|
||||||
|
minify_css_level_1,
|
||||||
|
minify_css_level_2,
|
||||||
|
minify_css_level_3,
|
||||||
minify_js,
|
minify_js,
|
||||||
remove_bangs,
|
remove_bangs,
|
||||||
remove_processing_instructions,
|
remove_processing_instructions,
|
||||||
|
|
|
@ -36,6 +36,9 @@ methods! {
|
||||||
keep_html_and_head_opening_tags: get_cfg_hash_prop!(cfg_hash, "keep_html_and_head_opening_tags"),
|
keep_html_and_head_opening_tags: get_cfg_hash_prop!(cfg_hash, "keep_html_and_head_opening_tags"),
|
||||||
keep_spaces_between_attributes: get_cfg_hash_prop!(cfg_hash, "keep_spaces_between_attributes"),
|
keep_spaces_between_attributes: get_cfg_hash_prop!(cfg_hash, "keep_spaces_between_attributes"),
|
||||||
minify_css: get_cfg_hash_prop!(cfg_hash, "minify_css"),
|
minify_css: get_cfg_hash_prop!(cfg_hash, "minify_css"),
|
||||||
|
minify_css_level_1: get_cfg_hash_prop!(cfg_hash, "minify_css_level_1"),
|
||||||
|
minify_css_level_2: get_cfg_hash_prop!(cfg_hash, "minify_css_level_2"),
|
||||||
|
minify_css_level_3: get_cfg_hash_prop!(cfg_hash, "minify_css_level_3"),
|
||||||
minify_js: get_cfg_hash_prop!(cfg_hash, "minify_js"),
|
minify_js: get_cfg_hash_prop!(cfg_hash, "minify_js"),
|
||||||
remove_bangs: get_cfg_hash_prop!(cfg_hash, "remove_bangs"),
|
remove_bangs: get_cfg_hash_prop!(cfg_hash, "remove_bangs"),
|
||||||
remove_processing_instructions: get_cfg_hash_prop!(cfg_hash, "remove_processing_instructions"),
|
remove_processing_instructions: get_cfg_hash_prop!(cfg_hash, "remove_processing_instructions"),
|
||||||
|
|
|
@ -50,8 +50,8 @@ impl Eq for AttrVal {}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Eq, PartialEq, Debug)]
|
||||||
pub enum RcdataContentType {
|
pub enum RcdataContentType {
|
||||||
Textarea,
|
Textarea,
|
||||||
Title,
|
Title,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive Eq for testing.
|
// Derive Eq for testing.
|
||||||
|
|
|
@ -14,9 +14,15 @@ pub struct Cfg {
|
||||||
pub keep_spaces_between_attributes: bool,
|
pub keep_spaces_between_attributes: bool,
|
||||||
/// Keep all comments.
|
/// Keep all comments.
|
||||||
pub keep_comments: bool,
|
pub keep_comments: bool,
|
||||||
/// If enabled, CSS in `<style>` tags and `style` attributes are minified.
|
/// Minify CSS in `<style>` tags and `style` attributes using [https://github.com/Mnwa/css-minify](css-minify). By default, the optimisation level is 1 as specified by the CSS minifier, but this can be adjusted by the minify_css_level_* settings.
|
||||||
pub minify_css: bool,
|
pub minify_css: bool,
|
||||||
/// If enabled, JavaScript in `<script>` tags are minified using
|
/// Use optimisation level 1 for the CSS minifier. This is currently the default, but may change in the future if higher levels become safe.
|
||||||
|
pub minify_css_level_1: bool,
|
||||||
|
/// Use optimisation level 2 for the CSS minifier. This is mostly safe, but may perform some dangerous optimisations.
|
||||||
|
pub minify_css_level_2: bool,
|
||||||
|
/// Use optimisation level 3 for the CSS minifier. This performs many dangerous optimisations, so ensure any input works with this level.
|
||||||
|
pub minify_css_level_3: bool,
|
||||||
|
/// Minify JavaScript in `<script>` tags using
|
||||||
/// [minify-js](https://github.com/wilsonzlin/minify-js).
|
/// [minify-js](https://github.com/wilsonzlin/minify-js).
|
||||||
///
|
///
|
||||||
/// Only `<script>` tags with a valid or no
|
/// Only `<script>` tags with a valid or no
|
||||||
|
|
|
@ -6,7 +6,19 @@ use css_minify::optimizations::{Level, Minifier};
|
||||||
|
|
||||||
pub fn minify_css(cfg: &Cfg, out: &mut Vec<u8>, code: &[u8]) {
|
pub fn minify_css(cfg: &Cfg, out: &mut Vec<u8>, code: &[u8]) {
|
||||||
if cfg.minify_css {
|
if cfg.minify_css {
|
||||||
let result = Minifier::default().minify(unsafe { from_utf8_unchecked(code) }, Level::Three);
|
let result = Minifier::default().minify(
|
||||||
|
unsafe { from_utf8_unchecked(code) },
|
||||||
|
if cfg.minify_css_level_1 {
|
||||||
|
Level::One
|
||||||
|
} else if cfg.minify_css_level_2 {
|
||||||
|
Level::Two
|
||||||
|
} else if cfg.minify_css_level_3 {
|
||||||
|
Level::Three
|
||||||
|
} else {
|
||||||
|
// Default to One, as other levels may perform dangerous optimisations.
|
||||||
|
Level::One
|
||||||
|
},
|
||||||
|
);
|
||||||
// TODO Collect error as warning.
|
// TODO Collect error as warning.
|
||||||
if let Ok(min) = result {
|
if let Ok(min) = result {
|
||||||
if min.len() < code.len() {
|
if min.len() < code.len() {
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::{ast::RcdataContentType, tag::{TAG_TEXTAREA_END, TAG_TITLE_END}, entity::encode::encode_entities};
|
use crate::{
|
||||||
|
ast::RcdataContentType,
|
||||||
|
entity::encode::encode_entities,
|
||||||
|
tag::{TAG_TEXTAREA_END, TAG_TITLE_END},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn minify_rcdata(out: &mut Vec<u8>, typ: RcdataContentType, text: &[u8]) {
|
pub fn minify_rcdata(out: &mut Vec<u8>, typ: RcdataContentType, text: &[u8]) {
|
||||||
// Encode entities, since they're still decoded by the browser.
|
// Encode entities, since they're still decoded by the browser.
|
||||||
|
@ -6,12 +10,16 @@ pub fn minify_rcdata(out: &mut Vec<u8>, typ: RcdataContentType, text: &[u8]) {
|
||||||
|
|
||||||
// Since the text has been decoded, there may be unintentional matches to end tags that we must escape.
|
// Since the text has been decoded, there may be unintentional matches to end tags that we must escape.
|
||||||
let html = match typ {
|
let html = match typ {
|
||||||
RcdataContentType::Textarea => &*TAG_TEXTAREA_END,
|
RcdataContentType::Textarea => &*TAG_TEXTAREA_END,
|
||||||
RcdataContentType::Title => &*TAG_TITLE_END,
|
RcdataContentType::Title => &*TAG_TITLE_END,
|
||||||
}.replace_all_bytes(&html, &[match typ {
|
}
|
||||||
RcdataContentType::Textarea => b"</textarea".as_slice(),
|
.replace_all_bytes(
|
||||||
RcdataContentType::Title => b"</title".as_slice(),
|
&html,
|
||||||
}]);
|
&[match typ {
|
||||||
|
RcdataContentType::Textarea => b"</textarea".as_slice(),
|
||||||
|
RcdataContentType::Title => b"</title".as_slice(),
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
|
||||||
out.extend_from_slice(&html);
|
out.extend_from_slice(&html);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue