timing.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /**
  2. * @fileoverview Tracks performance of individual rules.
  3. * @author Brandon Mills
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Helpers
  8. //------------------------------------------------------------------------------
  9. /* istanbul ignore next */
  10. /**
  11. * Align the string to left
  12. * @param {string} str string to evaluate
  13. * @param {int} len length of the string
  14. * @param {string} ch delimiter character
  15. * @returns {string} modified string
  16. * @private
  17. */
  18. function alignLeft(str, len, ch) {
  19. return str + new Array(len - str.length + 1).join(ch || " ");
  20. }
  21. /* istanbul ignore next */
  22. /**
  23. * Align the string to right
  24. * @param {string} str string to evaluate
  25. * @param {int} len length of the string
  26. * @param {string} ch delimiter character
  27. * @returns {string} modified string
  28. * @private
  29. */
  30. function alignRight(str, len, ch) {
  31. return new Array(len - str.length + 1).join(ch || " ") + str;
  32. }
  33. //------------------------------------------------------------------------------
  34. // Module definition
  35. //------------------------------------------------------------------------------
  36. const enabled = !!process.env.TIMING;
  37. const HEADERS = ["Rule", "Time (ms)", "Relative"];
  38. const ALIGN = [alignLeft, alignRight, alignRight];
  39. /* istanbul ignore next */
  40. /**
  41. * display the data
  42. * @param {Object} data Data object to be displayed
  43. * @returns {void} prints modified string with console.log
  44. * @private
  45. */
  46. function display(data) {
  47. let total = 0;
  48. const rows = Object.keys(data)
  49. .map(key => {
  50. const time = data[key];
  51. total += time;
  52. return [key, time];
  53. })
  54. .sort((a, b) => b[1] - a[1])
  55. .slice(0, 10);
  56. rows.forEach(row => {
  57. row.push(`${(row[1] * 100 / total).toFixed(1)}%`);
  58. row[1] = row[1].toFixed(3);
  59. });
  60. rows.unshift(HEADERS);
  61. const widths = [];
  62. rows.forEach(row => {
  63. const len = row.length;
  64. for (let i = 0; i < len; i++) {
  65. const n = row[i].length;
  66. if (!widths[i] || n > widths[i]) {
  67. widths[i] = n;
  68. }
  69. }
  70. });
  71. const table = rows.map(row => (
  72. row
  73. .map((cell, index) => ALIGN[index](cell, widths[index]))
  74. .join(" | ")
  75. ));
  76. table.splice(1, 0, widths.map((width, index) => {
  77. const extraAlignment = index !== 0 && index !== widths.length - 1 ? 2 : 1;
  78. return ALIGN[index](":", width + extraAlignment, "-");
  79. }).join("|"));
  80. console.log(table.join("\n")); // eslint-disable-line no-console
  81. }
  82. /* istanbul ignore next */
  83. module.exports = (function() {
  84. const data = Object.create(null);
  85. /**
  86. * Time the run
  87. * @param {*} key key from the data object
  88. * @param {Function} fn function to be called
  89. * @returns {Function} function to be executed
  90. * @private
  91. */
  92. function time(key, fn) {
  93. if (typeof data[key] === "undefined") {
  94. data[key] = 0;
  95. }
  96. return function(...args) {
  97. let t = process.hrtime();
  98. fn(...args);
  99. t = process.hrtime(t);
  100. data[key] += t[0] * 1e3 + t[1] / 1e6;
  101. };
  102. }
  103. if (enabled) {
  104. process.on("exit", () => {
  105. display(data);
  106. });
  107. }
  108. return {
  109. time,
  110. enabled
  111. };
  112. }());