"use strict"; 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(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: [], 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: true, sortClassName: true, trimCustomFragments: false, useShortDoctype: true, }).length, 'minimize': content => new minimize().parse(content).length, }; const colours = [ { backgroundColor: '#9ad0f5', borderColor: '#47aaec', }, { backgroundColor: '#ffb0c1', borderColor: '#ff87a1', }, { backgroundColor: '#a4dfdf', borderColor: '#4bc0c0', }, ]; const renderChart = async (file, title, cfg) => { const chart = new chartjs(435, 320); await chart.drawChart({ ...cfg, options: { title: { display: true, text: title, }, scales: { xAxes: [{ barPercentage: 0.5, gridLines: { color: '#ccc', }, ticks: { fontColor: '#222', }, }], yAxes: [{ gridLines: { color: '#666', }, ticks: { fontColor: '#222', }, }], }, legend: { labels: { fontFamily: 'Ubuntu, sans-serif', fontColor: '#000', }, }, }, }); await chart.writeImageToFile('image/png', path.join(__dirname, `${file}.png`)); }; const sizes = {}; const setSize = (program, test, result) => { if (!sizes[test]) { sizes[test] = { original: { absolute: tests.find(t => t.name === test).content.length, relative: 1, }, }; } const original = sizes[test].original.absolute; sizes[test][program] = { absolute: result, relative: result / original, }; }; // Run once to set sizes. for (const t of tests) { for (const p of Object.keys(programs)) { try { setSize(p, t.name, programs[p](t.content)); } catch (err) { console.error(`Failed to run ${p} on test ${t.name}:`); console.error(err); process.exit(1); } } } const suite = new benchmark.Suite(); for (const p of Object.keys(programs)) { suite.add(p, () => { for (const t of tests) { programs[p](t.content); } }); } suite .on('cycle', event => { console.info(event.target.toString()); }) .on('complete', async function () { const speedResults = this.map(b => ({ name: b.name, count: b.count, ops: b.hz, })).sort((a, b) => a.hz - b.hz); await renderChart('speed', 'Minification speed', { type: 'bar', data: { labels: speedResults.map(r => r.name), datasets: [{ label: 'Operations per second', ...colours[0], data: speedResults.map(r => r.ops), }], }, }); const testNames = Object.keys(sizes); const programNames = Object.keys(programs); await renderChart('minification', 'Relative minified HTML file size', { type: 'bar', scaleFontColor: 'red', data: { labels: testNames, datasets: programNames.map((program, i) => ({ label: program, ...colours[i], data: testNames.map(test => sizes[test][program].relative), })), }, }); }) .run({'async': true});