Modernise bench

This commit is contained in:
Wilson Lin 2021-08-07 20:53:28 +10:00
parent 94f292da38
commit b630f32087
6 changed files with 32 additions and 68 deletions

View File

@ -41,12 +41,11 @@ Since speed depends on the input, speed charts show performance relative to the
The settings used for each minifier can be found in [minifiers.js](./minifiers.js). Some settings to note: The settings used for each minifier can be found in [minifiers.js](./minifiers.js). Some settings to note:
- All minifiers are configured to use esbuild for JS minification asynchronously and in parallel, similar to how minify-html works.
- `conservativeCollapse` is enabled for html-minifier as otherwise some whitespace would be unsafely removed with side effects. minify-html can safely remove whitespace with context if configured properly. - `conservativeCollapse` is enabled for html-minifier as otherwise some whitespace would be unsafely removed with side effects. minify-html can safely remove whitespace with context if configured properly.
## Running ## Running
Make sure to install the dependencies listed in [package.json](./package.json) by running `npm i` or `yarn`. Node.js 10 is required, and system dependencies for building [canvas](https://www.npmjs.com/package/canvas), used for rendering graphs, may need to be installed. See the [npm package](https://www.npmjs.com/package/canvas) for more details. Make sure to install the dependencies listed in [package.json](./package.json) by running `npm i` or `yarn`.
Run [build.sh](./build.sh) to build @minify-html/js with the local minify-html. Run [build.sh](./build.sh) to build @minify-html/js with the local minify-html.

View File

@ -1,5 +1,5 @@
const chartjs = require('chartjs-node');
const results = require('./results'); const results = require('./results');
const request = require('request-promise-native');
const colours = { const colours = {
'minify-html': '#041f60', 'minify-html': '#041f60',
@ -50,9 +50,9 @@ const chartOptions = (title, displayLegend, yTick = t => t) => ({
}); });
const renderChart = async (cfg) => { const renderChart = async (cfg) => {
const chart = new chartjs(900, 650); return await request({
await chart.drawChart(cfg); url: `https://quickchart.io/chart?chart=${encodeURIComponent(JSON.stringify(cfg))}&width=900&height=650&format=png`,
return chart.getImageBuffer('image/png'); });
}; };
(async () => { (async () => {

View File

@ -25,32 +25,20 @@ const jsMime = new Set([
'text/x-javascript', 'text/x-javascript',
]); ]);
class EsbuildAsync {
constructor () {
this.promises = [];
}
queue (code, type) {
const id = this.promises.push(esbuild.transform(code, {
loader: type,
minify: true,
minifyWhitespace: true,
minifyIdentifiers: true,
minifySyntax: true,
})) - 1;
return `_____ESBUILD_ASYNC_PLACEHOLDER_${id}_____`;
}
async finalise (html) {
const jsTransformResults = await Promise.all(this.promises);
return html.replace(/_____ESBUILD_ASYNC_PLACEHOLDER_([0-9]+)_____/g, (_, id) => jsTransformResults[id].code.replace(/<\/script/g, "<\\/script"));
}
}
const minifyHtmlCfg = minifyHtml.createConfiguration({ const minifyHtmlCfg = minifyHtml.createConfiguration({
minifyJs: testJsAndCssMinification, minify_js: testJsAndCssMinification,
minifyCss: testJsAndCssMinification, minify_css: testJsAndCssMinification,
}); });
const esbuildCss = code => esbuild.transformSync(code, {
loader: "css",
minify: true,
}).code;
const esbuildJs = code => esbuild.transformSync(code, {
loader: "js",
minify: true,
}).code.replace(/<\/script/g, "<\\/script");
const htmlMinifierCfg = { const htmlMinifierCfg = {
collapseBooleanAttributes: true, collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true, collapseInlineTagWhitespace: true,
@ -63,9 +51,8 @@ const htmlMinifierCfg = {
decodeEntities: true, decodeEntities: true,
ignoreCustomComments: [], ignoreCustomComments: [],
ignoreCustomFragments: [/<\?[\s\S]*?\?>/], ignoreCustomFragments: [/<\?[\s\S]*?\?>/],
// These will be set later if `testJsAndCssMinification` is true. minifyCSS: testJsAndCssMinification && esbuildCss,
minifyCSS: false, minifyJS: testJsAndCssMinification && esbuildJs,
minifyJS: false,
processConditionalComments: true, processConditionalComments: true,
removeAttributeQuotes: true, removeAttributeQuotes: true,
removeComments: true, removeComments: true,
@ -79,39 +66,25 @@ const htmlMinifierCfg = {
}; };
module.exports = { module.exports = {
'@minify-html/js': (_, buffer) => minifyHtml.minifyInPlace(Buffer.from(buffer), minifyHtmlCfg), '@minify-html/js': (_, buffer) => minifyHtml.minify(Buffer.from(buffer), minifyHtmlCfg),
'html-minifier': testJsAndCssMinification 'html-minifier': content => htmlMinifier.minify(content, htmlMinifierCfg),
? async (content) => {
const js = new EsbuildAsync();
const res = htmlMinifier.minify(content, {
...htmlMinifierCfg,
minifyCSS: code => js.queue(code, 'css'),
minifyJS: code => js.queue(code, 'js'),
});
return js.finalise(res);
}
: content => htmlMinifier.minify(content, htmlMinifierCfg),
'minimize': testJsAndCssMinification 'minimize': testJsAndCssMinification
? async (content) => { ? (content) => new minimize({
const js = new EsbuildAsync();
const res = new minimize({
plugins: [ plugins: [
{ {
id: 'esbuild', id: 'esbuild',
element: (node, next) => { element: (node, next) => {
if (node.type === 'text' && node.parent) { if (node.type === 'text' && node.parent) {
if (node.parent.type === 'script' && jsMime.has(node.parent.attribs.type)) { if (node.parent.type === 'script' && jsMime.has(node.parent.attribs.type)) {
node.data = js.queue(node.data, 'js'); node.data = esbuildJs(node.data);
} else if (node.parent.type === 'style') { } else if (node.parent.type === 'style') {
node.data = js.queue(node.data, 'css'); node.data = esbuildCss(node.data);
} }
} }
next(); next();
}, },
}, },
], ],
}).parse(content); }).parse(content)
return js.finalise(res);
}
: content => new minimize().parse(content), : content => new minimize().parse(content),
}; };

View File

@ -3,18 +3,12 @@
"dependencies": { "dependencies": {
"@minify-html/js": "file:../nodejs", "@minify-html/js": "file:../nodejs",
"benchmark": "2.1.4", "benchmark": "2.1.4",
"chart.js": "^2.9.3", "esbuild": "^0.12.18",
"chartjs-node": "^1.7.1",
"esbuild": "^0.11.6",
"html-minifier": "4.0.0", "html-minifier": "4.0.0",
"minimize": "2.2.0", "minimize": "2.2.0",
"minimist": "^1.2.0", "minimist": "^1.2.5",
"mkdirp": "^0.5.1", "request": "^2.88.2",
"request": "^2.88.0", "request-promise-native": "^1.0.9"
"request-promise-native": "^1.0.8"
},
"engines": {
"node": "10.x"
}, },
"scripts": { "scripts": {
"start": "node bench.js" "start": "node bench.js"

View File

@ -1,8 +1,7 @@
const minifiers = require('./minifiers'); const minifiers = require('./minifiers');
const mkdirp = require('mkdirp');
const tests = require('./tests'); const tests = require('./tests');
const {join} = require('path'); const {join} = require('path');
const {readFileSync, writeFileSync} = require('fs'); const {mkdirSync, readFileSync, writeFileSync} = require('fs');
const RESULTS_DIR = join(__dirname, 'results'); const RESULTS_DIR = join(__dirname, 'results');
const SPEEDS_JSON = join(RESULTS_DIR, 'speeds.json'); const SPEEDS_JSON = join(RESULTS_DIR, 'speeds.json');
@ -15,7 +14,7 @@ const AVERAGE_SIZES_GRAPH = join(RESULTS_DIR, 'average-sizes.png');
const minifierNames = Object.keys(minifiers); const minifierNames = Object.keys(minifiers);
const testNames = tests.map(t => t.name); const testNames = tests.map(t => t.name);
mkdirp.sync(RESULTS_DIR); mkdirSync(RESULTS_DIR, {recursive: true});
module.exports = { module.exports = {
writeSpeedResults(speeds) { writeSpeedResults(speeds) {

View File

@ -1,5 +1,4 @@
const fs = require('fs'); const fs = require('fs');
const mkdirp = require('mkdirp');
const path = require('path'); const path = require('path');
const minifiers = require('./minifiers'); const minifiers = require('./minifiers');
const results = require('./results'); const results = require('./results');
@ -30,7 +29,7 @@ const setSize = (program, test, result) => {
// If `min` is a Buffer, convert to string (interpret as UTF-8) to get canonical length. // If `min` is a Buffer, convert to string (interpret as UTF-8) to get canonical length.
setSize(m, t.name, min.toString().length); setSize(m, t.name, min.toString().length);
const minPath = path.join(__dirname, 'min', m, `${t.name}.html`); const minPath = path.join(__dirname, 'min', m, `${t.name}.html`);
mkdirp.sync(path.dirname(minPath)); fs.mkdirSync(path.dirname(minPath), {recursive: true});
fs.writeFileSync(minPath, min); fs.writeFileSync(minPath, min);
} catch (err) { } catch (err) {
console.error(`Failed to run ${m} on test ${t.name}:`); console.error(`Failed to run ${m} on test ${t.name}:`);