github.com/wI2L/jettison@v0.7.5-0.20230106001914-c70014c6417a/tools/charts/index.js (about)

     1  #!/usr/bin/env node
     2  
     3  const fs = require('fs')
     4  const jd = require('jsdom')
     5  const {
     6      JSDOM
     7  } = jd
     8  const path = require('path')
     9  const prog = require('commander')
    10  const util = require('util')
    11  const deca = require('decamelize')
    12  const { optimize } = require('svgo');
    13  
    14  prog
    15      .option('-f, --input <file>', 'json-formatted benchmark data file')
    16      .option('-d, --directory <dir>', 'output directory of charts')
    17      .option('-n, --names <list>', 'comma-separated list of benchmark names', (value) => {
    18          return value.split(',')
    19      })
    20  
    21  prog.version('0.2.0')
    22  prog.parse(process.argv)
    23  
    24  // createChart creates and exports a Google Bar Chart
    25  // as a PNG image, using the decamelized form of name
    26  // with '-' as separator. The parameter data must be a
    27  // two-dimensional array where each row represents a
    28  // bar in the chart.
    29  function createBarChart (dom, name, data, opts) {
    30      var head = dom.window.document.getElementsByTagName('head')[0]
    31      var func = head.insertBefore
    32  
    33      // Prevent call to Google Font API.
    34      head.insertBefore = function (el, ref) {
    35          if (el.href && el.href.indexOf('//fonts.googleapis.com/css?family=Input') > -1) {
    36              return
    37          }
    38          func.call(head, el, ref)
    39      }
    40      // Add an event listener to draw the chart once
    41      // the DOM is fully loaded.
    42      dom.window.addEventListener('DOMContentLoaded', function () {
    43          const g = dom.window.google
    44  
    45          // Load the Google Visualization
    46          // API and the corechart package.
    47          g.charts.load('45.2', {
    48              packages: ['corechart', 'bar']
    49          })
    50          g.charts.setOnLoadCallback(function () {
    51              drawBarChart(dom, name, data, opts)
    52          })
    53      })
    54  }
    55  
    56  // exportChartAsSVG exports the SVG of the chart to
    57  // the current working directory. The file name is a
    58  // decamelized version of name using the hyphen char
    59  // as separator, in lowercase.
    60  function exportChartAsSVG (e, name) {
    61      var filename = util.format('%s.svg',
    62          path.join(prog.directory, deca(name, '-'))
    63      )
    64      var svgEl = e.getElementsByTagName('svg')[0]
    65      var svg = htmlToElement(svgEl.outerHTML)
    66  
    67      svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
    68      svg.setAttribute('version', '1.1')
    69  
    70      const result = optimize(svg.outerHTML, {
    71          plugins: [{
    72              name: "sortAttrs"
    73          }, {
    74              name: "removeAttrs",
    75              params: {
    76                  attrs: "(clip-path|aria-label|overflow)"
    77              }
    78          }],
    79          multipass: true
    80      })
    81      try {
    82          return fs.writeFileSync(filename, result.data)
    83      } catch (err) {
    84          console.error('cannot write svg chart %s: %s', filename, err)
    85      }
    86  }
    87  
    88  function htmlToElement (html) {
    89      const d = (new JSDOM('...')).window.document
    90      var t = d.createElement('template')
    91      t.innerHTML = html.trim()
    92      return t.content.firstChild
    93  }
    94  
    95  function drawBarChart (dom, name, data, opts) {
    96      const g = dom.window.google
    97      const d = dom.window.document
    98  
    99      // Convert the data 2D-array to a DataTable,
   100      // and sort it in alphabetical order using
   101      // the content of the first column which
   102      // contains the names of the benchmark runs.
   103      var dt = new g.visualization.arrayToDataTable(data)
   104      dt.sort([{
   105          column: 0
   106      }])
   107      var e = d.getElementById('chart')
   108      var c = new g.visualization.ColumnChart(e)
   109  
   110      // Setup a callback that exports the chart as
   111      // a PNG image when it has finished drawing.
   112      g.visualization.events.addListener(c, 'ready', function () {
   113          exportChartAsSVG(e, name)
   114      })
   115      c.draw(dt, opts)
   116  }
   117  
   118  // Load and parse the JSON-formatted benchmark
   119  // statistics from the input file.
   120  var file = fs.readFileSync(prog.input)
   121  var data = JSON.parse(file)
   122  
   123  // Iterate over all benchmarks and create the
   124  // chart only if it was requested through the
   125  // command-line parameters.
   126  Object.keys(data).forEach(function (key) {
   127      if (!prog.names.includes(key)) {
   128          return
   129      }
   130      const pageFile = path.join(__dirname, 'jsdom.html')
   131  
   132      JSDOM.fromFile(pageFile, {
   133          resources: 'usable',
   134          runScripts: 'dangerously',
   135          pretendToBeVisual: true
   136      }).then(dom => {
   137          createBarChart(dom, key, data[key], {
   138              width: 700,
   139              height: 400,
   140              chartArea: {
   141                  left: 100,
   142                  top: 50,
   143                  width: '70%',
   144                  height: '75%'
   145              },
   146              vAxis: {
   147                  format: '',
   148                  gridlines: {
   149                      count: 5
   150                  },
   151                  minorGridlines: {
   152                      count: 2
   153                  }
   154              },
   155              hAxis: {
   156                  textStyle: {
   157                      bold: true,
   158                      fontName: 'Input'
   159                  }
   160              }
   161          })
   162      })
   163  })