jsExecutionTransformer.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. var debug = require('debug')('ylt:jsExecutionTransformer');
  2. var offendersHelpers = require('../offendersHelpers');
  3. var jsExecutionTransformer = function() {
  4. this.transform = function(data) {
  5. var javascriptExecutionTree = {};
  6. var metrics = {
  7. domManipulations: 0,
  8. queriesWithoutResults: 0,
  9. jQueryCalls: 0,
  10. jQueryCallsOnEmptyObject: 0
  11. };
  12. debug('Starting JS execution transformation');
  13. try {
  14. javascriptExecutionTree = JSON.parse(data.toolsResults.phantomas.offenders.javascriptExecutionTree[0]);
  15. if (javascriptExecutionTree.children) {
  16. javascriptExecutionTree.children.forEach(function(node) {
  17. // Mark abnormal things with a warning flag
  18. var contextLenght = (node.data.callDetails && node.data.callDetails.context) ? node.data.callDetails.context.length : null;
  19. if ((node.data.type === 'jQuery - bind' && contextLenght > 5) ||
  20. node.data.resultsNumber === 0 ||
  21. contextLenght === 0) {
  22. node.warning = true;
  23. }
  24. // Mark errors with an error flag
  25. if (node.data.type === 'error' || node.data.type === 'jQuery version change') {
  26. node.error = true;
  27. }
  28. // Mark a performance flag
  29. if (['domInteractive', 'domContentLoaded', 'domContentLoadedEnd', 'domComplete'].indexOf(node.data.type) >= 0) {
  30. node.windowPerformance = true;
  31. }
  32. // Read the execution tree and adjust the navigation timings (cause their not very well synchronised)
  33. switch(node.data.type) {
  34. case 'domInteractive':
  35. data.toolsResults.phantomas.metrics.domInteractive = node.data.timestamp;
  36. break;
  37. case 'domContentLoaded':
  38. data.toolsResults.phantomas.metrics.domContentLoaded = node.data.timestamp;
  39. break;
  40. case 'domContentLoadedEnd':
  41. data.toolsResults.phantomas.metrics.domContentLoadedEnd = node.data.timestamp;
  42. break;
  43. case 'domComplete':
  44. data.toolsResults.phantomas.metrics.domComplete = node.data.timestamp;
  45. break;
  46. }
  47. // Change the list of dom paths into a tree
  48. treeRecursiveParser(node, function(node) {
  49. if (node.data.callDetails && node.data.callDetails.context && node.data.callDetails.context.length > 0) {
  50. node.data.callDetails.context.elements = node.data.callDetails.context.elements.map(offendersHelpers.domPathToDomElementObj, offendersHelpers);
  51. }
  52. if (node.data.type === 'appendChild' || node.data.type === 'insertBefore' || node.data.type === 'getComputedStyle') {
  53. node.data.callDetails.arguments[0] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[0]);
  54. }
  55. if (node.data.type === 'insertBefore') {
  56. node.data.callDetails.arguments[1] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[1]);
  57. }
  58. });
  59. });
  60. }
  61. debug('JS execution transformation complete');
  62. } catch(err) {
  63. throw err;
  64. }
  65. data.javascriptExecutionTree = javascriptExecutionTree;
  66. data.toolsResults.jsExecutionTransformer = {
  67. metrics: metrics
  68. };
  69. return data;
  70. };
  71. function treeRecursiveParser(node, fn) {
  72. if (node.children) {
  73. node.children.forEach(function(child) {
  74. treeRecursiveParser(child, fn);
  75. });
  76. }
  77. fn(node);
  78. }
  79. };
  80. module.exports = new jsExecutionTransformer();