broccoli-viz
Advanced tools
Comparing version
98
dot.js
@@ -1,5 +0,2 @@ | ||
// usage: DUMP_BROCCOLI_TREES=true broccoli serve | ||
// results: ./broccoli-tree.json | ||
// converting to dot: node $BROCCOLI_PATH/scripts/graph broccoli-trees.json > graph.dot | ||
// visualizing: dot -Tpng graph.dot -o graph.png | ||
var statsGlob = require('./stats-glob'); | ||
@@ -25,7 +22,2 @@ function formatTime(time) { | ||
function edgeColor(r) { | ||
var level = r + 1; | ||
return Math.max(1, Math.min(level, 4)); | ||
} | ||
function penWidth(level) { | ||
@@ -37,54 +29,66 @@ if (level === 0) return 3; | ||
function nodesById(nodes) { | ||
var result = new Array(nodes.length); | ||
nodes.forEach(function(node) { | ||
result[node.id] = node; | ||
function statsString(node, patterns) { | ||
var selfTime = node.stats.time.self; | ||
node.stats.time.total = node.stats._broccoli_viz.totalTime; | ||
var matchingStats = statsGlob(node.stats, patterns); | ||
var result = matchingStats.map(function (stat) { | ||
var key = stat.key; | ||
var value = stat.value; | ||
if (stat.isTime) { | ||
value = formatTime(value); | ||
} | ||
return ' ' + key + ' (' + value + ') \n'; | ||
}); | ||
return result; | ||
return result.join(''); | ||
} | ||
// reds8 | ||
module.exports = function dot(nodes) { | ||
module.exports = function dot(graph, options) { | ||
var out = 'digraph G {'; | ||
out += ' ratio = "auto"'; | ||
var byId = nodesById(nodes); | ||
var patterns = options && options.stats; | ||
nodes.map(function(node) { | ||
return node.toJSON(); | ||
}).forEach(function(node) { | ||
out += ' ' + node.id; | ||
var annotation = node.annotation || node.description; | ||
if (annotation) { | ||
annotation = annotation.replace('(', '\n('); | ||
if (!patterns) { | ||
patterns = ['time.self', 'time.total']; | ||
} | ||
var shape, style; | ||
graph.nodes.forEach(function(node) { | ||
var selfTime = node.stats.time.self; | ||
if (annotation.indexOf('Merge') > -1) { | ||
shape = 'circle'; | ||
style = 'dashed'; | ||
} else if (annotation.indexOf('Funnel') > -1) { | ||
shape = 'box'; | ||
style = 'dashed'; | ||
} else { | ||
shape = 'box'; | ||
style = 'solid'; | ||
} | ||
out += ' ' + node._id; | ||
var annotation = node.id.name; | ||
annotation = annotation.replace('(', '\n('); | ||
out += ' [shape=' + shape + ', style=' + style + ', colorscheme="rdylbu9", color=' + selfTimeColor(node.selfTime) +', label=" ' + | ||
node.id + ' \n' + | ||
annotation + '\n' + | ||
' self time (' + formatTime(node.selfTime) + ') \n' + | ||
' total time (' + formatTime(node.totalTime) + ')\n "]'; | ||
var shape, style; | ||
if (annotation.indexOf('Merge') > -1) { | ||
shape = 'circle'; | ||
style = 'dashed'; | ||
} else if (annotation.indexOf('Funnel') > -1) { | ||
shape = 'box'; | ||
style = 'dashed'; | ||
} else { | ||
out += ' [shape=circle, style="dotted", label=" ' + node.id + | ||
' self time (' + formatTime(node.selfTime) + | ||
')\n total time (' + formatTime(node.totalTime) + | ||
')" ]'; | ||
shape = 'box'; | ||
style = 'solid'; | ||
} | ||
out += ' [shape=' + shape + ', style=' + style + ', colorscheme="rdylbu9", color=' + selfTimeColor(selfTime) +', label=" ' + | ||
node._id + ' \n' + | ||
annotation + '\n' + | ||
statsString(node, patterns) + | ||
' "]'; | ||
out += '\n'; | ||
node.subtrees.forEach(function(child) { | ||
var level = node.level + byId[child].level; | ||
out += ' ' + child + ' -> ' + node.id + '[penwidth=' + penWidth(level) + ' ] \n'; | ||
node.children.forEach(function(childId) { | ||
// such doubts | ||
// var level = node.level + byId[child].level; | ||
var level = graph.nodesById[childId].stats._broccoli_viz.level; | ||
out += ' ' + childId + ' -> ' + node._id + '[penwidth=' + penWidth(level) + ' ] \n'; | ||
}); | ||
@@ -91,0 +95,0 @@ }); |
@@ -5,4 +5,3 @@ 'use strict'; | ||
dot: require('./dot'), | ||
flatten: require('./flatten'), | ||
process: require('./process') | ||
}; |
{ | ||
"name": "broccoli-viz", | ||
"version": "2.0.1", | ||
"version": "3.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"bin": { | ||
"ember": "./bin/broccoli-viz" | ||
}, | ||
"scripts": { | ||
"test": "mocha test" | ||
"test": "mocha test", | ||
"test:debug": "mocha --no-timeouts debug test/" | ||
}, | ||
@@ -20,4 +24,12 @@ "repository": { | ||
"devDependencies": { | ||
"mocha": "^2.0.1" | ||
"mocha": "^3.0.0" | ||
}, | ||
"dependencies": { | ||
"blank-object": "^1.0.0", | ||
"chalk": "^1.1.2", | ||
"deep-diff": "^0.3.4", | ||
"lodash": "^4.14.0", | ||
"minimist": "^1.2.0", | ||
"sprintf-js": "^1.0.3" | ||
} | ||
} |
var rank = require('./rank'); | ||
var flatten = require('./flatten'); | ||
var buildGraph = require('./build-graph'); | ||
module.exports = function(g) { | ||
return flatten(rank(g)); | ||
module.exports = function processNodes(nodes) { | ||
return rank(buildGraph(nodes)); | ||
}; |
60
rank.js
'use strict'; | ||
// largest to smaller | ||
function byTotalTime(x, y) { | ||
return y.totalTime - x.totalTime; | ||
function visitPreOrder(node, graph, parent, offset, cb) { | ||
cb(node, graph, parent, offset); | ||
node.children.forEach(function (id, i) { | ||
var child = graph.nodesById[id]; | ||
visitPreOrder(child, graph, node, i, cb); | ||
}); | ||
} | ||
function RankedNode(node, level) { | ||
this.id = node.id; | ||
this.level = level; | ||
this.node = node; | ||
this.subtrees = []; | ||
this.stats = {}; // Bucket for additional stats and metrics | ||
function annotateLevel(node, graph, parent, offset) { | ||
var parentLevel = typeof parent === 'object' ? parent.stats._broccoli_viz.level : 0; | ||
node.stats._broccoli_viz.level = parentLevel + offset; | ||
return node; | ||
} | ||
RankedNode.prototype.toJSON = function() { | ||
var json = this.node.toJSON(); | ||
json.level = this.level; | ||
json.stats = this.stats; | ||
return json; | ||
}; | ||
/** | ||
@param {graph} Object | ||
@param {graph.nodes} Array array of heimdall nodes by json order | ||
@param {graph.nodesById} Array array of heimdall nodes by id | ||
*/ | ||
module.exports = function rank(graph, theLevel) { | ||
function byTotalTime(childId1, childId2) { | ||
var c1 = graph.nodesById[childId1]; | ||
var c2 = graph.nodesById[childId2]; | ||
module.exports = function level(root, theLevel) { | ||
var currentLevel = arguments.length === 1 ? 0 : theLevel; | ||
var c1v = c1.stats._broccoli_viz; | ||
var c2v = c2.stats._broccoli_viz; | ||
// TODO: add ranking system | ||
var leveled = new RankedNode(root, currentLevel); | ||
return c2v.totalTime - c1v.totalTime; | ||
} | ||
var subtrees = root.subtrees; | ||
if (subtrees.length === 0 ) { return leveled; } | ||
function sortChildren(node) { | ||
node.children.sort(byTotalTime); | ||
} | ||
leveled.subtrees = subtrees.sort(byTotalTime).map(function(unleveled, i) { | ||
return level(unleveled, currentLevel + i); | ||
}); | ||
graph.nodes.forEach(sortChildren); | ||
return leveled; | ||
visitPreOrder(graph.nodes[0], graph, undefined, 0, annotateLevel); | ||
return graph; | ||
}; | ||
// |
42
set.js
@@ -0,6 +1,17 @@ | ||
var BlankObject = require('blank-object'); | ||
function getId(obj) { | ||
if (obj.id) { return obj.id; } | ||
return obj; | ||
} | ||
module.exports = Set; | ||
function Set() { | ||
function Set(initialValues) { | ||
this.values = []; | ||
this.map = Object.create(null); | ||
this.map = new BlankObject(); | ||
if (initialValues) { | ||
initialValues.forEach(this.add, this); | ||
} | ||
} | ||
@@ -13,5 +24,7 @@ | ||
Set.prototype.add = function(obj) { | ||
if (this.map[obj.id] !== true) { | ||
var id = getId(obj); | ||
if (this.map[id] !== true) { | ||
this.values.push(obj); | ||
this.map[obj.id] = true; | ||
this.map[id] = true; | ||
} | ||
@@ -23,5 +36,8 @@ | ||
Set.prototype.delete = function(obj) { | ||
if (this.map[obj.id] !== false) { | ||
this.values.push(obj); | ||
this.map[obj.id] = true; | ||
var id = getId(obj); | ||
if (this.map[id]) { | ||
var index = this.values.indexOf(obj); | ||
this.values.splice(index, 1); | ||
delete this.map[id]; | ||
} | ||
@@ -32,2 +48,14 @@ | ||
Set.prototype.deleteAll = function (values) { | ||
values.forEach(this.delete, this); | ||
return this; | ||
}; | ||
Set.prototype.setDiff = function (values) { | ||
var result = new this.constructor(this.values.slice()); | ||
result.deleteAll(values); | ||
return result; | ||
}; | ||
Set.prototype.forEach = function(_cb, binding) { | ||
@@ -34,0 +62,0 @@ var values = this.values; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
41444
229.42%20
81.82%1515
279.7%51
5000%6
Infinity%3
Infinity%70
-30%2
Infinity%2
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added