Improve closing tag minification; update bench results
This commit is contained in:
parent
82a2e6e983
commit
1cb9bf9817
|
@ -15,7 +15,7 @@ Available as:
|
|||
|
||||
## Performance
|
||||
|
||||
Speed and effectiveness of Node.js version compared to other JS minifiers.
|
||||
Speed and effectiveness of Node.js version compared to [html-minfier](https://github.com/kangax/html-minifier) and [minimize](https://github.com/Swaagie/minimize).
|
||||
|
||||
![Chart showing speed of HTML minifiers](./bench/speed.png) ![Chart showing effectiveness of HTML minifiers](./bench/minification.png)
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
node_modules/
|
||||
min/
|
||||
|
|
|
@ -1,57 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
const benchmark = require("benchmark");
|
||||
const benchmark = require('benchmark');
|
||||
const chartjs = require('chartjs-node');
|
||||
const fs = require("fs");
|
||||
const htmlMinifier = require("html-minifier");
|
||||
const hyperbuild = require("hyperbuild");
|
||||
const minimize = require("minimize");
|
||||
const path = require("path");
|
||||
|
||||
const testsDir = path.join(__dirname, "tests");
|
||||
const tests = fs.readdirSync(testsDir).map(name => ({
|
||||
name,
|
||||
content: fs.readFileSync(path.join(testsDir, name), "utf8"),
|
||||
}));
|
||||
|
||||
const programs = {
|
||||
'hyperbuild-nodejs': content => hyperbuild.minify_in_place(Buffer.from(content)),
|
||||
'html-minifier': content => htmlMinifier.minify(content, {
|
||||
caseSensitive: false,
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
collapseWhitespace: true,
|
||||
conservativeWhitespace: false,
|
||||
customEventAttributes: [],
|
||||
decodeEntities: true,
|
||||
html5: true,
|
||||
ignoreCustomComments: [],
|
||||
ignoreCustomFragments: [/<\?[\s\S]*?\?>/],
|
||||
includeAutoGeneratedTags: true,
|
||||
keepClosingSlash: false,
|
||||
minifyCSS: false,
|
||||
minifyJS: false,
|
||||
minifyURLs: false,
|
||||
preserveLineBreaks: false,
|
||||
preventAttributesEscaping: false,
|
||||
processConditionalComments: true,
|
||||
processScripts: [],
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: false,
|
||||
removeEmptyElements: false,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
removeTagWhitespace: true,
|
||||
sortAttributes: false,
|
||||
sortClassName: false,
|
||||
trimCustomFragments: false,
|
||||
useShortDoctype: true,
|
||||
}).length,
|
||||
'minimize': content => new minimize().parse(content).length,
|
||||
};
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const programs = require('./minifiers');
|
||||
const tests = require('./tests');
|
||||
|
||||
const colours = [
|
||||
{
|
||||
|
@ -129,7 +83,7 @@ const setSize = (program, test, result) => {
|
|||
for (const t of tests) {
|
||||
for (const p of Object.keys(programs)) {
|
||||
try {
|
||||
setSize(p, t.name, programs[p](t.content));
|
||||
setSize(p, t.name, programs[p](t.content).length);
|
||||
} catch (err) {
|
||||
console.error(`Failed to run ${p} on test ${t.name}:`);
|
||||
console.error(err);
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 367738,
|
||||
"relative": 0.6420287198289032
|
||||
"absolute": 362616,
|
||||
"relative": 0.6330862904281787
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 488839,
|
||||
|
@ -23,8 +23,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 233690,
|
||||
"relative": 0.5830777944394404
|
||||
"absolute": 224376,
|
||||
"relative": 0.559838517716393
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 298773,
|
||||
|
@ -41,8 +41,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 98036,
|
||||
"relative": 0.6298045110849859
|
||||
"absolute": 96856,
|
||||
"relative": 0.6222239353466829
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 137061,
|
||||
|
@ -59,8 +59,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 272938,
|
||||
"relative": 0.7387984354487258
|
||||
"absolute": 271250,
|
||||
"relative": 0.7342292960872684
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 270621,
|
||||
|
@ -77,8 +77,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 82104,
|
||||
"relative": 0.6514329239264972
|
||||
"absolute": 79380,
|
||||
"relative": 0.6298200514138818
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 79394,
|
||||
|
@ -95,8 +95,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 232603,
|
||||
"relative": 0.5920112597765861
|
||||
"absolute": 232058,
|
||||
"relative": 0.5906241489629755
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 383586,
|
||||
|
@ -113,8 +113,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 32033,
|
||||
"relative": 0.5724983468268011
|
||||
"absolute": 28860,
|
||||
"relative": 0.5157900380676639
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 28464,
|
||||
|
@ -131,8 +131,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 1391016,
|
||||
"relative": 0.7014896018348458
|
||||
"absolute": 1383721,
|
||||
"relative": 0.6978107321127253
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 1888047,
|
||||
|
@ -149,8 +149,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 865816,
|
||||
"relative": 0.5611814537429229
|
||||
"absolute": 831178,
|
||||
"relative": 0.5387307214917895
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 1115700,
|
||||
|
@ -167,8 +167,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 90282,
|
||||
"relative": 0.5828443049987411
|
||||
"absolute": 86946,
|
||||
"relative": 0.5613076908178878
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 89329,
|
||||
|
@ -185,8 +185,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 279810,
|
||||
"relative": 0.930210137531873
|
||||
"absolute": 270831,
|
||||
"relative": 0.9003600363028295
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 273191,
|
||||
|
@ -203,8 +203,8 @@
|
|||
"relative": 1
|
||||
},
|
||||
"hyperbuild-nodejs": {
|
||||
"absolute": 1535741,
|
||||
"relative": 0.6294206222478697
|
||||
"absolute": 1347041,
|
||||
"relative": 0.5520822745589214
|
||||
},
|
||||
"html-minifier": {
|
||||
"absolute": 1307604,
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
@ -0,0 +1,42 @@
|
|||
const htmlMinifier = require("html-minifier");
|
||||
const hyperbuild = require("hyperbuild");
|
||||
const minimize = require("minimize");
|
||||
|
||||
module.exports = {
|
||||
'hyperbuild-nodejs': content => hyperbuild.minify(Buffer.from(content)),
|
||||
'html-minifier': content => htmlMinifier.minify(content, {
|
||||
caseSensitive: false,
|
||||
collapseBooleanAttributes: true,
|
||||
collapseInlineTagWhitespace: true,
|
||||
collapseWhitespace: true,
|
||||
conservativeWhitespace: false,
|
||||
customEventAttributes: [],
|
||||
decodeEntities: true,
|
||||
html5: true,
|
||||
ignoreCustomComments: [],
|
||||
ignoreCustomFragments: [/<\?[\s\S]*?\?>/],
|
||||
includeAutoGeneratedTags: true,
|
||||
keepClosingSlash: false,
|
||||
minifyCSS: false,
|
||||
minifyJS: false,
|
||||
minifyURLs: false,
|
||||
preserveLineBreaks: false,
|
||||
preventAttributesEscaping: false,
|
||||
processConditionalComments: true,
|
||||
processScripts: [],
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: false,
|
||||
removeEmptyElements: false,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
removeTagWhitespace: true,
|
||||
sortAttributes: false,
|
||||
sortClassName: false,
|
||||
trimCustomFragments: false,
|
||||
useShortDoctype: true,
|
||||
}),
|
||||
'minimize': content => new minimize().parse(content),
|
||||
};
|
|
@ -7,6 +7,7 @@
|
|||
"html-minifier": "3.5.19",
|
||||
"hyperbuild": "file:../nodejs",
|
||||
"minimize": "2.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"prettier": "^1.19.1",
|
||||
"request": "^2.88.0",
|
||||
"request-promise-native": "^1.0.8"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
const fs = require('fs');
|
||||
const mkdirp = require('mkdirp');
|
||||
const path = require('path');
|
||||
const programs = require('./minifiers');
|
||||
const tests = require('./tests');
|
||||
|
||||
for (const t of tests) {
|
||||
for (const p of Object.keys(programs)) {
|
||||
try {
|
||||
const minPath = path.join(__dirname, 'min', p, t.name);
|
||||
mkdirp.sync(path.dirname(minPath));
|
||||
fs.writeFileSync(minPath, programs[p](t.content));
|
||||
} catch (err) {
|
||||
console.error(`Failed to run ${p} on test ${t.name}:`);
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
[
|
||||
{
|
||||
"name": "hyperbuild-nodejs",
|
||||
"ops": 13.777334566242345
|
||||
"ops": 10.158435796714862
|
||||
},
|
||||
{
|
||||
"name": "html-minifier",
|
||||
"ops": 0.9361359019226175
|
||||
"ops": 0.9598865156998558
|
||||
},
|
||||
{
|
||||
"name": "minimize",
|
||||
"ops": 3.537829104467651
|
||||
"ops": 3.6888006698975837
|
||||
}
|
||||
]
|
BIN
bench/speed.png
BIN
bench/speed.png
Binary file not shown.
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.5 KiB |
|
@ -0,0 +1,8 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const testsDir = path.join(__dirname, "tests");
|
||||
module.exports = fs.readdirSync(testsDir).map(name => ({
|
||||
name,
|
||||
content: fs.readFileSync(path.join(testsDir, name), "utf8"),
|
||||
}));
|
|
@ -45,13 +45,15 @@ enum TagType {
|
|||
|
||||
pub struct ProcessedTag {
|
||||
pub name: ProcessorRange,
|
||||
pub closing_tag: Option<ProcessorRange>,
|
||||
pub has_closing_tag: bool,
|
||||
}
|
||||
|
||||
impl ProcessedTag {
|
||||
pub fn write_closing_tag(&self, proc: &mut Processor) -> () {
|
||||
if let Some(tag) = self.closing_tag {
|
||||
proc.write_range(tag);
|
||||
if self.has_closing_tag {
|
||||
proc.write_slice(b"</");
|
||||
proc.write_range(self.name);
|
||||
proc.write(b'>');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +155,7 @@ pub fn process_tag(proc: &mut Processor, prev_sibling_closing_tag: Option<Proces
|
|||
// Write discarded tag closing characters.
|
||||
if is_void_tag { proc.write_slice(b">"); } else { proc.write_slice(b"/>"); };
|
||||
};
|
||||
return Ok(ProcessedTag { name: tag_name, closing_tag: None });
|
||||
return Ok(ProcessedTag { name: tag_name, has_closing_tag: false });
|
||||
};
|
||||
|
||||
match tag_type {
|
||||
|
@ -163,10 +165,9 @@ pub fn process_tag(proc: &mut Processor, prev_sibling_closing_tag: Option<Proces
|
|||
};
|
||||
|
||||
// Require closing tag for non-void.
|
||||
let closing_tag = proc.checkpoint();
|
||||
chain!(proc.match_seq(b"</").require()?.discard());
|
||||
chain!(proc.match_seq(b"</").require_with_reason("closing tag")?.discard());
|
||||
chain!(proc.match_while_pred(is_valid_tag_name_char).require_with_reason("closing tag name")?.discard());
|
||||
chain!(proc.match_while_pred(is_whitespace).discard());
|
||||
chain!(proc.match_char(b'>').require()?.discard());
|
||||
Ok(ProcessedTag { name: tag_name, closing_tag: Some(proc.consumed_range(closing_tag)) })
|
||||
Ok(ProcessedTag { name: tag_name, has_closing_tag: true })
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue