Skip to content

Commit

Permalink
Some code cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
pahen committed Apr 18, 2014
1 parent 8e0d917 commit d976942
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 83 deletions.
9 changes: 5 additions & 4 deletions bin/madge
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
var fs = require('fs'),
version = require('../package.json').version,
program = require('commander'),
printResult = require('../lib/print'),
madge = require('../lib/madge');

program
Expand Down Expand Up @@ -76,21 +77,21 @@ function run() {

// Ouput summary
if (program.summary) {
require('../lib/print').summary(res.obj(), {
printResult.summary(res.obj(), {
colors: program.colors,
output: program.output
});

// Output circular dependencies
} else if (program.circular) {
require('../lib/print').circular(res.circular(), {
printResult.circular(res.circular(), {
colors: program.colors,
output: program.output
});

// Output module dependencies
} else if (program.depends) {
require('../lib/print').depends(res.depends(program.depends), {
printResult.depends(res.depends(program.depends), {
colors: program.colors,
output: program.output
});
Expand All @@ -117,7 +118,7 @@ function run() {

// Output text (default)
} else {
require('../lib/print').list(res.obj(), {
printResult.list(res.obj(), {
colors: program.colors,
output: program.output
});
Expand Down
20 changes: 0 additions & 20 deletions lib/analysis/depends.js

This file was deleted.

66 changes: 42 additions & 24 deletions lib/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Module dependencies.
*/
var exec = require('child_process').exec,
circular = require('./analysis/circular'),
graphviz = require('graphviz'),
g = graphviz.digraph('G');

Expand Down Expand Up @@ -38,6 +39,39 @@ function checkGraphvizInstalled() {
});
}

/**
* Return options to use with graphviz digraph.
* @param {Object} opts
* @return {Object}
*/
function createGraphvizOptions(opts) {
// Valid attributes: http://www.graphviz.org/doc/info/attrs.html
var G = {
layout: opts.layout || 'dot',
overlap: false,
bgcolor: '#ffffff'
},
N = {
fontname: opts.fontFace || 'Times-Roman',
fontsize: opts.fontSize || 14
},
E = {};

if (opts.colors) {
G.bgcolor = opts.imageColors.bgcolor || '#000000';
E.color = opts.imageColors.edge || '#757575';
N.color = opts.imageColors.dependencies || '#c6c5fe';
N.fontcolor = opts.imageColors.fontColor || opts.imageColors.dependencies || '#c6c5fe';
}

return {
'type': 'png',
'G': G,
'E': E,
'N': N
};
}

/**
* Creates a PNG image from the module dependency graph.
* @param {Object} modules
Expand All @@ -47,48 +81,32 @@ function checkGraphvizInstalled() {
module.exports.image = function (modules, opts, callback) {
checkGraphvizInstalled();

opts.imageColors = opts.imageColors || {};

var nodes = {},
circular = require('./analysis/circular')(modules),
colors = opts.imageColors || {};
circularResult = circular(modules);

Object.keys(modules).forEach(function (id) {

nodes[id] = nodes[id] || g.addNode(id);
if (opts.colors && modules[id]) {
if (!modules[id].length) {
noDependencyNode(nodes[id], colors.noDependencies);
} else if (circular.isCyclic(id)) {
nodeColor(nodes[id], (colors.circular || '#ff6c60'));
noDependencyNode(nodes[id], opts.imageColors.noDependencies);
} else if (circularResult.isCyclic(id)) {
nodeColor(nodes[id], (opts.imageColors.circular || '#ff6c60'));
}
}

modules[id].forEach(function (depId) {
nodes[depId] = nodes[depId] || g.addNode(depId);
if (opts.colors && !modules[depId]) {
noDependencyNode(nodes[depId], colors.noDependencies);
noDependencyNode(nodes[depId], opts.imageColors.noDependencies);
}
g.addEdge(nodes[id], nodes[depId]);
});

});

// Valid attributes: http://www.graphviz.org/doc/info/attrs.html
g.output({
'type': 'png',
'G' : {
'layout': opts.layout || 'dot',
'overlap': false,
'bgcolor': opts.colors ? (colors.bgcolor || '#000000') : '#ffffff'
},
'E' : opts.colors ? {'color': (colors.edge || '#757575') } : {},
'N' : opts.colors ? {
'color': (colors.dependencies || '#c6c5fe'),
'fontcolor': (colors.fontColor || colors.dependencies || '#c6c5fe'),
'fontname' : opts.fontFace || 'Times-Roman',
'fontsize': opts.fontSize || 14
} : {}
}, callback);

g.output(createGraphvizOptions(opts), callback);
};

/**
Expand Down
40 changes: 28 additions & 12 deletions lib/madge.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
/**
* Module dependencies.
*/
var requirejs = require('./requirejs');
var requirejs = require('./requirejs'),
circular = require('./analysis/circular'),
CJS = require('./parse/cjs'),
AMD = require('./parse/amd'),
graph = require('./graph');

/**
* Merge the two given trees.
Expand Down Expand Up @@ -58,13 +62,13 @@ function Madge(src, opts) {
}

if (src && src.length) {
var Parser = require('./parse/' + this.opts.format);

if (!Parser) {
if (this.opts.format === 'cjs') {
tree = new CJS(src, this.opts).tree;
} else if (this.opts.format === 'amd') {
tree = new AMD(src, this.opts).tree;
} else {
throw new Error('invalid module format "' + this.opts.format + '"');
}

tree = new Parser(src, this.opts).tree;
}

if (this.opts.requireConfig) {
Expand All @@ -89,17 +93,29 @@ Madge.prototype.obj = function () {
* @return {Object}
*/
Madge.prototype.circular = function () {
return require('./analysis/circular')(this.tree);
return circular(this.tree);
};

/**
* Return a list of modules that depends on the given module.
* @api public
* @param {String} id
* @return {Array}
* @param {Object} opts
* @return {Array|Object}
*/
Madge.prototype.depends = function (id) {
return require('./analysis/depends')(this.tree, id);
Madge.prototype.depends = function (id, opts) {
opts = opts || {};

return Object.keys(this.tree).filter(function (module) {
if (this.tree[module]) {
return this.tree[module].reduce(function (acc, dependency) {
if (dependency === id) {
acc = module;
}
return acc;
}, false);
}
}, this);
};

/**
Expand All @@ -108,7 +124,7 @@ Madge.prototype.depends = function (id) {
* @return {String}
*/
Madge.prototype.dot = function () {
return require('./graph').dot(this.tree);
return graph.dot(this.tree);
};

/**
Expand All @@ -118,5 +134,5 @@ Madge.prototype.dot = function () {
* @param {Function} callback
*/
Madge.prototype.image = function (opts, callback) {
require('./graph').image(this.tree, opts, callback);
graph.image(this.tree, opts, callback);
};
47 changes: 34 additions & 13 deletions test/amd.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ var should = require('should'),
describe('module format (AMD)', function () {

it('should behave as expected on ok files', function () {
madge([__dirname + '/files/amd/ok'], {format: 'amd'}).obj().should.eql({ a: [ 'sub/b' ], d: [], e: [ 'sub/c' ], 'sub/b': [ 'sub/c' ], 'sub/c': [ 'd' ] });
madge([__dirname + '/files/amd/ok'], {
format: 'amd'
}).obj().should.eql({ 'a': [ 'sub/b' ], 'd': [], 'e': [ 'sub/c' ], 'sub/b': [ 'sub/c' ], 'sub/c': [ 'd' ] });
});

it('should handle optimized files', function () {
madge([__dirname + '/files/amd/a-built.js'], {format: 'amd', optimized: true}).obj().should.eql({ a: [ 'sub/b' ], d: [], 'sub/b': [ 'sub/c' ], 'sub/c': [ 'd' ] });
madge([__dirname + '/files/amd/a-built.js'], {
format: 'amd',
optimized: true
}).obj().should.eql({ 'a': [ 'sub/b' ], 'd': [], 'sub/b': [ 'sub/c' ], 'sub/c': [ 'd' ] });
});

it('should merge in shim dependencies found in RequireJS config', function () {
Expand All @@ -22,44 +27,60 @@ describe('module format (AMD)', function () {
madge([__dirname + '/files/amd/ok'], {
format: 'amd',
exclude: '^sub'
}).obj().should.eql({ a: [], d: [], e: [] });
}).obj().should.eql({ 'a': [], 'd': [], 'e': [] });

madge([__dirname + '/files/amd/ok'], {
format: 'amd',
exclude: '.*\/c$'
}).obj().should.eql({ a: [ 'sub/b' ], d: [], e: [], 'sub/b': [] });
}).obj().should.eql({ 'a': [ 'sub/b' ], 'd': [], 'e': [], 'sub/b': [] });
});

it('should tackle errors in files', function () {
madge([__dirname + '/files/amd/error.js'], {format: 'amd'}).obj().should.eql({ error: [] });
madge([__dirname + '/files/amd/error.js'], {
format: 'amd'
}).obj().should.eql({ error: [] });
});

it('should handle id different than file', function () {
madge([__dirname + '/files/amd/namedWrapped/diff.js'], {format: 'amd'}).obj().should.eql({ ffid: [] });
madge([__dirname + '/files/amd/namedWrapped/diff.js'], {
format: 'amd'
}).obj().should.eql({ 'ffid': [] });
});

it('should handle named modules', function () {
madge([__dirname + '/files/amd/namedWrapped/car.js'], {format: 'amd'}).obj().should.eql({ car: [ 'engine', 'wheels' ] });
madge([__dirname + '/files/amd/namedWrapped/car.js'], {
format: 'amd'
}).obj().should.eql({ 'car': [ 'engine', 'wheels' ] });
});

it('should find circular dependencies', function () {
madge([__dirname + '/files/amd/circular'], {format: 'amd'}).circular().getArray().should.eql([ ['a', 'c'], ['f', 'g', 'h'] ]);
madge([__dirname + '/files/amd/circular'], {
format: 'amd'
}).circular().getArray().should.eql([ ['a', 'c'], ['f', 'g', 'h'] ]);
});

it('should find modules that depends on another', function () {
madge([__dirname + '/files/amd/ok'], {format: 'amd'}).depends('sub/c').should.eql([ 'e', 'sub/b' ]);
madge([__dirname + '/files/amd/ok'], {
format: 'amd'
}).depends('sub/c').should.eql([ 'e', 'sub/b' ]);
});

it('should compile coffeescript on-the-fly', function () {
madge([__dirname + '/files/amd/coffeescript'], {format: 'amd'}).obj().should.eql({ a: ['b'], b: [] });
madge([__dirname + '/files/amd/coffeescript'], {
format: 'amd'
}).obj().should.eql({ 'a': ['b'], 'b': [] });
});

it('should resolve relative module indentifiers', function () {
madge([__dirname + '/files/amd/relative'], {format: 'amd'}).obj().should.eql({ a: [], b: [ 'a' ], 'foo/bar/d': [ 'a' ], 'foo/c': [ 'a' ] });
madge([__dirname + '/files/amd/relative'], {
format: 'amd'
}).obj().should.eql({ 'a': [], 'b': [ 'a' ], 'foo/bar/d': [ 'a' ], 'foo/c': [ 'a' ] });
});

it('should ignore plugins', function () {
madge([__dirname + '/files/amd/plugin.js'], {format: 'amd', breakOnError: true}).obj().should.eql({ plugin: [ 'ok/a' ] });
madge([__dirname + '/files/amd/plugin.js'], {
format: 'amd',
breakOnError: true
}).obj().should.eql({ plugin: [ 'ok/a' ] });
});

});
Loading

0 comments on commit d976942

Please sign in to comment.