Comparing version 0.5.5 to 0.6.0
@@ -10,5 +10,5 @@ 'use strict'; | ||
function getPath(parent, unresolved) { | ||
var parentVisited = false; | ||
let parentVisited = false; | ||
return Object.keys(unresolved).filter(function (module) { | ||
return Object.keys(unresolved).filter((module) => { | ||
if (module === parent) { | ||
@@ -34,3 +34,3 @@ parentVisited = true; | ||
if (modules[id]) { | ||
modules[id].forEach(function (dependency) { | ||
modules[id].forEach((dependency) => { | ||
if (!resolved[dependency]) { | ||
@@ -56,7 +56,7 @@ if (unresolved[dependency]) { | ||
module.exports = function (modules) { | ||
var circular = []; | ||
var resolved = {}; | ||
var unresolved = {}; | ||
const circular = []; | ||
const resolved = {}; | ||
const unresolved = {}; | ||
Object.keys(modules).forEach(function (id) { | ||
Object.keys(modules).forEach((id) => { | ||
resolver(id, modules, circular, resolved, unresolved); | ||
@@ -70,3 +70,3 @@ }); | ||
*/ | ||
getArray: function () { | ||
getArray() { | ||
return circular; | ||
@@ -80,5 +80,5 @@ }, | ||
*/ | ||
isCyclic: function (id) { | ||
var cyclic = false; | ||
circular.forEach(function (path) { | ||
isCyclic(id) { | ||
let cyclic = false; | ||
circular.forEach((path) => { | ||
if (path.indexOf(id) >= 0) { | ||
@@ -85,0 +85,0 @@ cyclic = true; |
'use strict'; | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var exec = require('child_process').exec; | ||
var cyclic = require('./cyclic'); | ||
var graphviz = require('graphviz'); | ||
const exec = require('child_process').exec; | ||
const cyclic = require('./cyclic'); | ||
const graphviz = require('graphviz'); | ||
@@ -34,3 +31,3 @@ /** | ||
function checkGraphvizInstalled() { | ||
exec('gvpr -V', function (error, stdout, stderr) { | ||
exec('gvpr -V', (error, stdout, stderr) => { | ||
if (error !== null) { | ||
@@ -49,3 +46,3 @@ throw new Error('Graphviz could not be found. Ensure that "gvpr" is in your $PATH.\n' + error); | ||
// Valid attributes: http://www.graphviz.org/doc/info/attrs.html | ||
var G = { | ||
const G = { | ||
layout: opts.layout || 'dot', | ||
@@ -56,3 +53,3 @@ overlap: false, | ||
var N = { | ||
const N = { | ||
fontname: opts.fontFace || 'Times-Roman', | ||
@@ -62,3 +59,3 @@ fontsize: opts.fontSize || 14 | ||
var E = {}; | ||
const E = {}; | ||
@@ -87,3 +84,4 @@ if (opts.colors) { | ||
module.exports.image = function (modules, opts, callback) { | ||
var g = graphviz.digraph('G'); | ||
const g = graphviz.digraph('G'); | ||
const nodes = {}; | ||
@@ -94,6 +92,5 @@ checkGraphvizInstalled(); | ||
var nodes = {}; | ||
var cyclicResults = cyclic(modules); | ||
const cyclicResults = cyclic(modules); | ||
Object.keys(modules).forEach(function (id) { | ||
Object.keys(modules).forEach((id) => { | ||
@@ -109,3 +106,3 @@ nodes[id] = nodes[id] || g.addNode(id); | ||
modules[id].forEach(function (depId) { | ||
modules[id].forEach((depId) => { | ||
nodes[depId] = nodes[depId] || g.addNode(depId); | ||
@@ -128,11 +125,11 @@ if (opts.colors && !modules[depId]) { | ||
module.exports.dot = function (modules) { | ||
var nodes = {}; | ||
var g = graphviz.digraph('G'); | ||
const nodes = {}; | ||
const g = graphviz.digraph('G'); | ||
checkGraphvizInstalled(); | ||
Object.keys(modules).forEach(function (id) { | ||
Object.keys(modules).forEach((id) => { | ||
nodes[id] = nodes[id] || g.addNode(id); | ||
modules[id].forEach(function (depId) { | ||
modules[id].forEach((depId) => { | ||
nodes[depId] = nodes[depId] || g.addNode(depId); | ||
@@ -139,0 +136,0 @@ g.addEdge(nodes[id], nodes[depId]); |
202
lib/madge.js
'use strict'; | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var cyclic = require('./cyclic'); | ||
var CJS = require('./parse/cjs'); | ||
var AMD = require('./parse/amd'); | ||
var ES6 = require('./parse/es6'); | ||
var graph = require('./graph'); | ||
const cyclic = require('./cyclic'); | ||
const CJS = require('./parse/cjs'); | ||
const AMD = require('./parse/amd'); | ||
const ES6 = require('./parse/es6'); | ||
const graph = require('./graph'); | ||
/** | ||
* Expose factory function. | ||
* @api public | ||
* @param {String|Array|Object} src | ||
* @param {Object} opts | ||
* @return {Madge} | ||
*/ | ||
module.exports = function (src, opts) { | ||
return new Madge(src, opts); | ||
}; | ||
class Madge { | ||
/** | ||
* Class constructor. | ||
* @constructor | ||
* @api public | ||
* @param {String|Array|Object} src | ||
* @param {Object} opts | ||
*/ | ||
constructor(src, opts) { | ||
let tree = []; | ||
/** | ||
* Class constructor. | ||
* @constructor | ||
* @api public | ||
* @param {String|Array|Object} src | ||
* @param {Object} opts | ||
*/ | ||
function Madge(src, opts) { | ||
var tree = []; | ||
this.opts = opts || {}; | ||
this.opts.format = String(this.opts.format || 'cjs').toLowerCase(); | ||
this.opts = opts || {}; | ||
this.opts.format = String(this.opts.format || 'cjs').toLowerCase(); | ||
if (typeof src === 'object' && !Array.isArray(src)) { | ||
this.tree = src; | ||
return; | ||
} | ||
if (typeof src === 'object' && !Array.isArray(src)) { | ||
this.tree = src; | ||
return; | ||
if (typeof src === 'string') { | ||
src = [src]; | ||
} | ||
if (src && src.length) { | ||
tree = this.parse(src); | ||
} | ||
this.tree = tree; | ||
} | ||
if (typeof src === 'string') { | ||
src = [src]; | ||
/** | ||
* Parse the given source folder(s). | ||
* @param {Array|Object} src | ||
* @return {Object} | ||
*/ | ||
parse(src) { | ||
if (this.opts.format === 'cjs') { | ||
return new CJS(src, this.opts, this).tree; | ||
} else if (this.opts.format === 'amd') { | ||
return new AMD(src, this.opts, this).tree; | ||
} else if (this.opts.format === 'es6') { | ||
return new ES6(src, this.opts, this).tree; | ||
} else { | ||
throw new Error('invalid module format "' + this.opts.format + '"'); | ||
} | ||
} | ||
if (src && src.length) { | ||
tree = this.parse(src); | ||
/** | ||
* Return the module dependency graph as an object. | ||
* @api public | ||
* @return {Object} | ||
*/ | ||
obj() { | ||
return this.tree; | ||
} | ||
this.tree = tree; | ||
} | ||
/** | ||
* Return the modules that has circular dependencies. | ||
* @api public | ||
* @return {Object} | ||
*/ | ||
circular() { | ||
return cyclic(this.tree); | ||
} | ||
/** | ||
* Parse the given source folder(s). | ||
* @param {Array|Object} src | ||
* @return {Object} | ||
*/ | ||
Madge.prototype.parse = function (src) { | ||
if (this.opts.format === 'cjs') { | ||
return new CJS(src, this.opts, this).tree; | ||
} else if (this.opts.format === 'amd') { | ||
return new AMD(src, this.opts, this).tree; | ||
} else if (this.opts.format === 'es6') { | ||
return new ES6(src, this.opts, this).tree; | ||
} else { | ||
throw new Error('invalid module format "' + this.opts.format + '"'); | ||
/** | ||
* Return a list of modules that depends on the given module. | ||
* @api public | ||
* @param {String} id | ||
* @return {Array|Object} | ||
*/ | ||
depends(id) { | ||
return Object.keys(this.tree).filter((module) => { | ||
if (this.tree[module]) { | ||
return this.tree[module].reduce((acc, dependency) => { | ||
if (dependency === id) { | ||
acc = module; | ||
} | ||
return acc; | ||
}, false); | ||
} | ||
}); | ||
} | ||
}; | ||
/** | ||
* Return the module dependency graph as an object. | ||
* @api public | ||
* @return {Object} | ||
*/ | ||
Madge.prototype.obj = function () { | ||
return this.tree; | ||
}; | ||
/** | ||
* Return the module dependency graph as DOT output. | ||
* @api public | ||
* @return {String} | ||
*/ | ||
dot() { | ||
return graph.dot(this.tree); | ||
} | ||
/** | ||
* Return the modules that has circular dependencies. | ||
* @api public | ||
* @return {Object} | ||
*/ | ||
Madge.prototype.circular = function () { | ||
return cyclic(this.tree); | ||
}; | ||
/** | ||
* Return the module dependency graph as a PNG image. | ||
* @api public | ||
* @param {Object} opts | ||
* @param {Function} callback | ||
*/ | ||
image(opts, callback) { | ||
graph.image(this.tree, opts, callback); | ||
} | ||
} | ||
/** | ||
* Return a list of modules that depends on the given module. | ||
* @api public | ||
* @param {String} id | ||
* @return {Array|Object} | ||
*/ | ||
Madge.prototype.depends = function (id) { | ||
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); | ||
}; | ||
/** | ||
* Return the module dependency graph as DOT output. | ||
* @api public | ||
* @return {String} | ||
*/ | ||
Madge.prototype.dot = function () { | ||
return graph.dot(this.tree); | ||
}; | ||
/** | ||
* Return the module dependency graph as a PNG image. | ||
* @api public | ||
* @param {Object} opts | ||
* @param {Function} callback | ||
*/ | ||
Madge.prototype.image = function (opts, callback) { | ||
graph.image(this.tree, opts, callback); | ||
}; | ||
module.exports = (src, opts) => new Madge(src, opts); |
'use strict'; | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var util = require('util'); | ||
var amdetective = require('amdetective'); | ||
var parse = require('./parse'); | ||
var Base = require('./base'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const amdetective = require('amdetective'); | ||
const parse = require('./parse'); | ||
const Base = require('./base'); | ||
@@ -19,3 +15,3 @@ /** | ||
function mergeTrees(a, b) { | ||
Object.keys(b).forEach(function (id) { | ||
Object.keys(b).forEach((id) => { | ||
if (!a[id]) { | ||
@@ -25,3 +21,3 @@ a[id] = []; | ||
b[id].forEach(function (dep) { | ||
b[id].forEach((dep) => { | ||
if (a[id].indexOf(dep) < 0) { | ||
@@ -41,3 +37,3 @@ a[id].push(dep); | ||
function convertPathsToIds(deps, pathDefs, baseDir) { | ||
var path, pathDeps, i1, len1, i2, len2; | ||
let path, pathDeps, i1, len1, i2, len2; | ||
@@ -50,3 +46,3 @@ if (baseDir) { | ||
Object.keys(pathDefs).forEach(function (id) { | ||
Object.keys(pathDefs).forEach((id) => { | ||
path = pathDefs[id]; | ||
@@ -83,3 +79,3 @@ | ||
// normalize entries within deps-arrays (i.e. replace path-refs with id-refs) | ||
Object.keys(pathDefs).forEach(function (id) { | ||
Object.keys(pathDefs).forEach((id) => { | ||
path = baseDir + pathDefs[id]; | ||
@@ -105,6 +101,6 @@ if (deps[id]) { | ||
function getShimDepsFromConfig(filename, exclude) { | ||
var deps = {}; | ||
var config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); // eslint-disable-line no-sync | ||
var excludeRegex = exclude ? new RegExp(exclude) : false; | ||
var isIncluded = function (key) { | ||
const deps = {}; | ||
const config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); // eslint-disable-line no-sync | ||
const excludeRegex = exclude ? new RegExp(exclude) : false; | ||
const isIncluded = function (key) { | ||
return !(excludeRegex && key.match(excludeRegex)); | ||
@@ -114,3 +110,3 @@ }; | ||
if (config.shim) { | ||
Object.keys(config.shim).filter(isIncluded).forEach(function (key) { | ||
Object.keys(config.shim).filter(isIncluded).forEach((key) => { | ||
if (config.shim[key].deps) { | ||
@@ -134,6 +130,6 @@ deps[key] = config.shim[key].deps.filter(isIncluded); | ||
function getPathsFromConfig(filename, exclude) { | ||
var paths = {}; | ||
var config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); // eslint-disable-line no-sync | ||
var excludeRegex = exclude ? new RegExp(exclude) : false; | ||
var isIncluded = function (key) { | ||
const paths = {}; | ||
const config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); // eslint-disable-line no-sync | ||
const excludeRegex = exclude ? new RegExp(exclude) : false; | ||
const isIncluded = function (key) { | ||
return !(excludeRegex && key.match(excludeRegex)); | ||
@@ -143,3 +139,3 @@ }; | ||
if (config.paths) { | ||
Object.keys(config.paths).filter(isIncluded).forEach(function (key) { | ||
Object.keys(config.paths).filter(isIncluded).forEach((key) => { | ||
paths[key] = config.paths[key]; | ||
@@ -159,60 +155,55 @@ }); | ||
function getBaseUrlFromConfig(filename, srcBaseDir) { | ||
var config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); // eslint-disable-line no-sync | ||
const config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); // eslint-disable-line no-sync | ||
return config.baseUrl ? path.relative(srcBaseDir, config.baseUrl) : ''; | ||
} | ||
/** | ||
* This class will parse the AMD module format. | ||
* @see https://github.com/amdjs/amdjs-api/wiki/AMD | ||
* @constructor | ||
* @param {Array} src | ||
* @param {Object} opts | ||
* @param {Object} parent | ||
*/ | ||
var AMD = module.exports = function (src, opts, parent) { | ||
Base.apply(this, arguments); | ||
class AMD extends Base { | ||
/** | ||
* @constructor | ||
* @param {Array} src | ||
* @param {Object} opts | ||
* @param {Object} parent | ||
*/ | ||
constructor(src, opts, parent) { | ||
super(src, opts, parent); | ||
if (opts.requireConfig) { | ||
var baseDir = src.length ? src[0].replace(/\\/g, '/') : ''; | ||
baseDir = getBaseUrlFromConfig(opts.requireConfig, baseDir); | ||
convertPathsToIds(this.tree, getPathsFromConfig(opts.requireConfig, opts.exclude), baseDir); | ||
mergeTrees(this.tree, getShimDepsFromConfig(opts.requireConfig, opts.exclude)); | ||
if (opts.requireConfig) { | ||
let baseDir = src.length ? src[0].replace(/\\/g, '/') : ''; | ||
baseDir = getBaseUrlFromConfig(opts.requireConfig, baseDir); | ||
convertPathsToIds(this.tree, getPathsFromConfig(opts.requireConfig, opts.exclude), baseDir); | ||
mergeTrees(this.tree, getShimDepsFromConfig(opts.requireConfig, opts.exclude)); | ||
} | ||
} | ||
}; | ||
/** | ||
* Inherit from `Base` | ||
*/ | ||
util.inherits(AMD, Base); | ||
/** | ||
* Normalize a module file path and return a proper identificator. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
normalize(filename) { | ||
return this.replaceBackslashInPath(path.relative(this.baseDir, filename).replace(this.extRegEx, '')); | ||
} | ||
/** | ||
* Normalize a module file path and return a proper identificator. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
AMD.prototype.normalize = function (filename) { | ||
return this.replaceBackslashInPath(path.relative(this.baseDir, filename).replace(this.extRegEx, '')); | ||
}; | ||
/** | ||
* Parse the given file and return all found dependencies. | ||
* @param {String} filename | ||
* @return {Array} | ||
*/ | ||
parseFile(filename) { | ||
const dependencies = []; | ||
const src = this.getFileSource(filename); | ||
/** | ||
* Parse the given file and return all found dependencies. | ||
* @param {String} filename | ||
* @return {Array} | ||
*/ | ||
AMD.prototype.parseFile = function (filename) { | ||
try { | ||
var dependencies = []; | ||
var src = this.getFileSource(filename); | ||
var fileData = {filename: filename, src: src}; | ||
this.emit('parseFile', { | ||
filename: filename, | ||
src: src | ||
}); | ||
this.emit('parseFile', fileData); | ||
if (/define|require\s*\(/m.test(fileData.src)) { | ||
amdetective(fileData.src, {findNestedDependencies: this.opts.findNestedDependencies}).map(function (obj) { | ||
if (/define|require\s*\(/m.test(src)) { | ||
amdetective(src, {findNestedDependencies: this.opts.findNestedDependencies}).map((obj) => { | ||
return typeof obj === 'string' ? [obj] : obj.deps; | ||
}).filter(function (deps) { | ||
deps.filter(function (id) { | ||
}).filter((deps) => { | ||
deps.filter((id) => { | ||
// Ignore RequireJS IDs and plugins | ||
return id !== 'require' && id !== 'exports' && id !== 'module' && !id.match(/\.?\w\!/); | ||
}).map(function (id) { | ||
}).map((id) => { | ||
// Only resolve relative module identifiers (if the first term is "." or "..") | ||
@@ -223,3 +214,3 @@ if (id.charAt(0) !== '.') { | ||
var depFilename = path.resolve(path.dirname(fileData.filename), id); | ||
const depFilename = path.resolve(path.dirname(filename), id); | ||
@@ -229,62 +220,56 @@ if (depFilename) { | ||
} | ||
}, this).forEach(function (id) { | ||
}).forEach((id) => { | ||
if (!this.isExcluded(id) && dependencies.indexOf(id) < 0) { | ||
dependencies.push(id); | ||
} | ||
}, this); | ||
}, this); | ||
}); | ||
}); | ||
} | ||
return dependencies; | ||
} | ||
} catch (e) { | ||
if (this.opts.breakOnError) { | ||
console.log(String('\nError while parsing file: ' + filename).red); | ||
throw e; | ||
} | ||
return dependencies; | ||
} | ||
return []; | ||
}; | ||
/** | ||
* Get module dependencies from optimize file (r.js). | ||
* @param {String} filename | ||
*/ | ||
addOptimizedModules(filename) { | ||
const anonymousRequire = []; | ||
/** | ||
* Get module dependencies from optimize file (r.js). | ||
* @param {String} filename | ||
*/ | ||
AMD.prototype.addOptimizedModules = function (filename) { | ||
var self = this; | ||
var anonymousRequire = []; | ||
amdetective(this.getFileSource(filename)) | ||
.filter((obj) => { | ||
const id = obj.name || obj; | ||
return id !== 'require' && id !== 'exports' && id !== 'module' && !id.match(/\.?\w\!/) && !this.isExcluded(id); | ||
}) | ||
.forEach((obj) => { | ||
if (typeof obj === 'string') { | ||
anonymousRequire.push(obj); | ||
return; | ||
} | ||
amdetective(this.getFileSource(filename)) | ||
.filter(function (obj) { | ||
var id = obj.name || obj; | ||
return id !== 'require' && id !== 'exports' && id !== 'module' && !id.match(/\.?\w\!/) && !self.isExcluded(id); | ||
}) | ||
.forEach(function (obj) { | ||
if (typeof obj === 'string') { | ||
anonymousRequire.push(obj); | ||
return; | ||
} | ||
if (!this.isExcluded(obj.name)) { | ||
this.tree[obj.name] = obj.deps.filter((id) => { | ||
return id !== 'require' && id !== 'exports' && id !== 'module' && !id.match(/\.?\w\!/) && !this.isExcluded(id); | ||
}); | ||
} | ||
}); | ||
if (!self.isExcluded(obj.name)) { | ||
self.tree[obj.name] = obj.deps.filter(function (id) { | ||
return id !== 'require' && id !== 'exports' && id !== 'module' && !id.match(/\.?\w\!/) && !self.isExcluded(id); | ||
}); | ||
} | ||
}); | ||
if (anonymousRequire.length > 0) { | ||
this.tree[this.opts.mainRequireModule || ''] = anonymousRequire; | ||
} | ||
} | ||
if (anonymousRequire.length > 0) { | ||
this.tree[this.opts.mainRequireModule || ''] = anonymousRequire; | ||
/** | ||
* Parse the given `filename` and add it to the module tree. | ||
* @param {String} filename | ||
*/ | ||
addModule(filename) { | ||
if (this.opts.optimized) { | ||
this.addOptimizedModules(filename); | ||
} else { | ||
Base.prototype.addModule.call(this, filename); | ||
} | ||
} | ||
}; | ||
} | ||
/** | ||
* Parse the given `filename` and add it to the module tree. | ||
* @param {String} filename | ||
*/ | ||
AMD.prototype.addModule = function (filename) { | ||
if (this.opts.optimized) { | ||
this.addOptimizedModules(filename); | ||
} else { | ||
Base.prototype.addModule.call(this, filename); | ||
} | ||
}; | ||
module.exports = AMD; |
'use strict'; | ||
/** | ||
* Module dependencies | ||
*/ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var util = require('util'); | ||
var resolve = require('resolve'); | ||
var EventEmitter = require('events').EventEmitter; | ||
var commondir = require('commondir'); | ||
var finder = require('walkdir'); | ||
var coffee = require('coffee-script'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const resolve = require('resolve'); | ||
const EventEmitter = require('events').EventEmitter; | ||
const commondir = require('commondir'); | ||
const finder = require('walkdir'); | ||
const coffee = require('coffee-script'); | ||
/** | ||
* Traversing `src` and fetches all dependencies. | ||
* @constructor | ||
* @param {Array} src | ||
* @param {Object} opts | ||
* @param {Object} parent | ||
*/ | ||
var Base = module.exports = function (src, opts, parent) { | ||
if (opts.onParseFile) { | ||
this.on('parseFile', opts.onParseFile.bind(parent)); | ||
} | ||
class Base extends EventEmitter { | ||
/** | ||
* Traversing `src` and fetches all dependencies. | ||
* @constructor | ||
* @param {Array} src | ||
* @param {Object} opts | ||
* @param {Object} parent | ||
*/ | ||
constructor(src, opts, parent) { | ||
super(); | ||
if (opts.onAddModule) { | ||
this.on('addModule', opts.onAddModule.bind(parent)); | ||
} | ||
if (opts.onParseFile) { | ||
this.on('parseFile', opts.onParseFile.bind(parent)); | ||
} | ||
this.opts = opts; | ||
if (opts.onAddModule) { | ||
this.on('addModule', opts.onAddModule.bind(parent)); | ||
} | ||
if (typeof this.opts.extensions === 'undefined') { | ||
this.opts.extensions = ['.js']; | ||
} | ||
this.opts = opts; | ||
this.tree = {}; | ||
this.extRegEx = new RegExp('\\.(coffee|jsx|' + this.opts.extensions.map(function (str) { | ||
return str.substring(1); | ||
}).join('|') + ')$', 'g'); | ||
this.coffeeExtRegEx = /\.coffee$/; | ||
src = this.resolveTargets(src); | ||
this.excludeRegex = opts.exclude ? new RegExp(opts.exclude) : false; | ||
this.baseDir = this.getBaseDir(src); | ||
this.readFiles(src); | ||
this.sortDependencies(); | ||
}; | ||
if (typeof this.opts.extensions === 'undefined') { | ||
this.opts.extensions = ['.js']; | ||
} | ||
util.inherits(Base, EventEmitter); | ||
this.tree = {}; | ||
this.extRegEx = new RegExp('\\.(coffee|jsx|' + this.opts.extensions.map((str) => { | ||
return str.substring(1); | ||
}).join('|') + ')$', 'g'); | ||
this.coffeeExtRegEx = /\.coffee$/; | ||
src = this.resolveTargets(src); | ||
this.excludeRegex = opts.exclude ? new RegExp(opts.exclude) : false; | ||
this.baseDir = this.getBaseDir(src); | ||
this.readFiles(src); | ||
this.sortDependencies(); | ||
} | ||
/** | ||
* Resolve the given `id` to a filename. | ||
* @param {String} dir | ||
* @param {String} id | ||
* @return {String} | ||
*/ | ||
Base.prototype.resolve = function (dir, id) { | ||
try { | ||
return resolve.sync(id, { | ||
basedir: dir, | ||
paths: this.opts.paths, | ||
extensions: this.opts.extensions | ||
}); | ||
} catch (e) { | ||
if (this.opts.breakOnError) { | ||
console.log(String('\nError while resolving module from: ' + id).red); | ||
throw e; | ||
/** | ||
* Resolve the given `id` to a filename. | ||
* @param {String} dir | ||
* @param {String} id | ||
* @return {String} | ||
*/ | ||
resolve(dir, id) { | ||
try { | ||
return resolve.sync(id, { | ||
basedir: dir, | ||
paths: this.opts.paths, | ||
extensions: this.opts.extensions | ||
}); | ||
} catch (e) { | ||
if (this.opts.breakOnError) { | ||
console.log(String('\nError while resolving module from: ' + id).red); | ||
throw e; | ||
} | ||
return id; | ||
} | ||
return id; | ||
} | ||
}; | ||
/** | ||
* Get the most common dir from the `src`. | ||
* @param {Array} src | ||
* @return {String} | ||
*/ | ||
Base.prototype.getBaseDir = function (src) { | ||
var dir = commondir(src); | ||
/** | ||
* Get the most common dir from the `src`. | ||
* @param {Array} src | ||
* @return {String} | ||
*/ | ||
getBaseDir(src) { | ||
const dir = commondir(src); | ||
if (!fs.statSync(dir).isDirectory()) { // eslint-disable-line no-sync | ||
dir = path.dirname(dir); | ||
if (!fs.statSync(dir).isDirectory()) { // eslint-disable-line no-sync | ||
return path.dirname(dir); | ||
} | ||
return dir; | ||
} | ||
return dir; | ||
}; | ||
/** | ||
* Resolves all paths in `sources` and ensure we have a absolute path. | ||
* @param {Array} sources | ||
* @return {Array} | ||
*/ | ||
Base.prototype.resolveTargets = function (sources) { | ||
return sources.map(function (src) { | ||
return path.resolve(src); | ||
}); | ||
}; | ||
/** | ||
* Resolves all paths in `sources` and ensure we have a absolute path. | ||
* @param {Array} sources | ||
* @return {Array} | ||
*/ | ||
resolveTargets(sources) { | ||
return sources.map((src) => path.resolve(src)); | ||
} | ||
/** | ||
* Normalize a module file path and return a proper identificator. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
Base.prototype.normalize = function (filename) { | ||
return this.replaceBackslashInPath(path.relative(this.baseDir, filename).replace(this.extRegEx, '')); | ||
}; | ||
/** | ||
* Normalize a module file path and return a proper identificator. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
normalize(filename) { | ||
return this.replaceBackslashInPath(path.relative(this.baseDir, filename).replace(this.extRegEx, '')); | ||
} | ||
/** | ||
* Check if module should be excluded. | ||
* @param {String} id | ||
* @return {Boolean} | ||
*/ | ||
Base.prototype.isExcluded = function (id) { | ||
return this.excludeRegex && id.match(this.excludeRegex); | ||
}; | ||
/** | ||
* Check if module should be excluded. | ||
* @param {String} id | ||
* @return {Boolean} | ||
*/ | ||
isExcluded(id) { | ||
return this.excludeRegex && id.match(this.excludeRegex); | ||
} | ||
/** | ||
* Parse the given `filename` and add it to the module tree. | ||
* @param {String} filename | ||
*/ | ||
Base.prototype.addModule = function (filename) { | ||
var id = this.normalize(filename); | ||
/** | ||
* Parse the given `filename` and add it to the module tree. | ||
* @param {String} filename | ||
*/ | ||
addModule(filename) { | ||
const id = this.normalize(filename); | ||
if (!this.isExcluded(id) && fs.existsSync(filename)) { // eslint-disable-line no-sync | ||
this.tree[id] = this.parseFile(filename); | ||
this.emit('addModule', {id: id, dependencies: this.tree[id]}); | ||
if (!this.isExcluded(id) && fs.existsSync(filename)) { // eslint-disable-line no-sync | ||
try { | ||
this.tree[id] = this.parseFile(filename); | ||
this.emit('addModule', {id: id, dependencies: this.tree[id]}); | ||
} catch (e) { | ||
if (this.opts.breakOnError) { | ||
console.log(String('\nError while parsing file: ' + filename).red); | ||
throw e; | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Traverse `sources` and parse files found. | ||
* @param {Array} sources | ||
*/ | ||
Base.prototype.readFiles = function (sources) { | ||
sources.forEach(function (src) { | ||
if (fs.statSync(src).isDirectory()) { // eslint-disable-line no-sync | ||
finder.sync(src).filter(function (filename) { | ||
return filename.match(this.extRegEx); | ||
}, this).forEach(function (filename) { | ||
this.addModule(filename); | ||
}, this); | ||
} else { | ||
this.addModule(src); | ||
/** | ||
* Traverse `sources` and parse files found. | ||
* @param {Array} sources | ||
*/ | ||
readFiles(sources) { | ||
sources.forEach((src) => { | ||
if (fs.statSync(src).isDirectory()) { // eslint-disable-line no-sync | ||
finder.sync(src).filter((filename) => { | ||
return filename.match(this.extRegEx); | ||
}).forEach((filename) => { | ||
this.addModule(filename); | ||
}); | ||
} else { | ||
this.addModule(src); | ||
} | ||
}); | ||
} | ||
/** | ||
* Read the given filename and compile it if necessary and return the content. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
getFileSource(filename) { | ||
const src = fs.readFileSync(filename, 'utf8'); // eslint-disable-line no-sync | ||
if (filename.match(this.coffeeExtRegEx)) { | ||
return coffee.compile(src, { | ||
header: false, | ||
bare: true | ||
}); | ||
} | ||
}, this); | ||
}; | ||
/** | ||
* Read the given filename and compile it if necessary and return the content. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
Base.prototype.getFileSource = function (filename) { | ||
var src = fs.readFileSync(filename, 'utf8'); // eslint-disable-line no-sync | ||
return src; | ||
} | ||
if (filename.match(this.coffeeExtRegEx)) { | ||
return coffee.compile(src, { | ||
header: false, | ||
bare: true | ||
}); | ||
/** | ||
* Sort dependencies by name. | ||
*/ | ||
sortDependencies() { | ||
this.tree = Object.keys(this.tree).sort().reduce((acc, id) => { | ||
(acc[id] = this.tree[id]).sort(); | ||
return acc; | ||
}, {}); | ||
} | ||
return src; | ||
}; | ||
/** | ||
* Replace back slashes in path (Windows) with forward slashes (*nix). | ||
* @param {String} path | ||
* @return {String} | ||
*/ | ||
replaceBackslashInPath(path) { | ||
return path.replace(/\\/g, '/'); | ||
} | ||
} | ||
/** | ||
* Sort dependencies by name. | ||
*/ | ||
Base.prototype.sortDependencies = function () { | ||
var self = this; | ||
this.tree = Object.keys(this.tree).sort().reduce(function (acc, id) { | ||
(acc[id] = self.tree[id]).sort(); | ||
return acc; | ||
}, {}); | ||
}; | ||
/** | ||
* Replace back slashes in path (Windows) with forward slashes (*nix). | ||
* @param {String} path | ||
* @return {String} | ||
*/ | ||
Base.prototype.replaceBackslashInPath = function (path) { | ||
return path.replace(/\\/g, '/'); | ||
}; | ||
module.exports = Base; |
'use strict'; | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var util = require('util'); | ||
var detective = require('detective'); | ||
var Base = require('./base'); | ||
const path = require('path'); | ||
const detective = require('detective'); | ||
const Base = require('./base'); | ||
/** | ||
* This class will parse the CommonJS module format. | ||
* @see http://nodejs.org/api/modules.html | ||
* @constructor | ||
*/ | ||
var CJS = module.exports = function () { | ||
Base.apply(this, arguments); | ||
}; | ||
/** | ||
* Inherit from `Base`. | ||
*/ | ||
util.inherits(CJS, Base); | ||
/** | ||
* Normalize a module file path and return a proper identificator. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
CJS.prototype.normalize = function (filename) { | ||
filename = this.replaceBackslashInPath(filename); | ||
if (filename.charAt(0) !== '/' && !filename.match(/^[A-Za-z:]+\//i)) { | ||
// a core module (not mapped to a file) | ||
return filename; | ||
class CJS extends Base { | ||
/** | ||
* Normalize a module file path and return a proper identificator. | ||
* @param {String} filename | ||
* @return {String} | ||
*/ | ||
normalize(filename) { | ||
filename = this.replaceBackslashInPath(filename); | ||
if (filename.charAt(0) !== '/' && !filename.match(/^[A-Za-z:]+\//i)) { | ||
// a core module (not mapped to a file) | ||
return filename; | ||
} | ||
return super.normalize(filename); | ||
} | ||
return Base.prototype.normalize.apply(this, arguments); | ||
}; | ||
/** | ||
* Parse the given file and return all found dependencies. | ||
* @param {String} filename | ||
* @return {Array} | ||
*/ | ||
CJS.prototype.parseFile = function (filename) { | ||
try { | ||
if (fs.existsSync(filename)) { // eslint-disable-line no-sync | ||
var dependencies = []; | ||
var src = this.getFileSource(filename); | ||
var fileData = {filename: filename, src: src}; | ||
/** | ||
* Parse the given file and return all found dependencies. | ||
* @param {String} filename | ||
* @return {Array} | ||
*/ | ||
parseFile(filename) { | ||
const dependencies = []; | ||
const src = this.getFileSource(filename); | ||
this.emit('parseFile', fileData); | ||
this.emit('parseFile', { | ||
filename: filename, | ||
src: src | ||
}); | ||
if (/require\s*\(/m.test(fileData.src)) { | ||
detective(fileData.src).map(function (id) { | ||
var depFilename = this.resolve(path.dirname(fileData.filename), id); | ||
if (depFilename) { | ||
return this.normalize(depFilename); | ||
} | ||
}, this).filter(function (id) { | ||
if (!this.isExcluded(id) && dependencies.indexOf(id) < 0) { | ||
dependencies.push(id); | ||
} | ||
}, this); | ||
if (/require\s*\(/m.test(src)) { | ||
detective(src).map((id) => { | ||
const depFilename = this.resolve(path.dirname(filename), id); | ||
if (depFilename) { | ||
return this.normalize(depFilename); | ||
} | ||
}).filter((id) => { | ||
if (!this.isExcluded(id) && dependencies.indexOf(id) < 0) { | ||
dependencies.push(id); | ||
} | ||
}); | ||
} | ||
return dependencies; | ||
} | ||
} | ||
} catch (e) { | ||
if (this.opts.breakOnError) { | ||
console.log(String('\nError while parsing file: ' + filename).red); | ||
throw e; | ||
} | ||
return dependencies; | ||
} | ||
} | ||
return []; | ||
}; | ||
module.exports = CJS; |
'use strict'; | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var util = require('util'); | ||
var detective = require('detective-es6'); | ||
var Base = require('./base'); | ||
const path = require('path'); | ||
const detective = require('detective-es6'); | ||
const Base = require('./base'); | ||
/** | ||
* This class will parse the ES6 module format. | ||
* @see http://nodejs.org/api/modules.html | ||
* @constructor | ||
*/ | ||
var ES6 = module.exports = function () { | ||
Base.apply(this, arguments); | ||
}; | ||
class ES6 extends Base { | ||
/** | ||
* Parse the given file and return all found dependencies. | ||
* @param {String} filename | ||
* @return {Array} | ||
*/ | ||
parseFile(filename) { | ||
const dependencies = []; | ||
const src = this.getFileSource(filename); | ||
/** | ||
* Inherit from `Base`. | ||
*/ | ||
util.inherits(ES6, Base); | ||
this.emit('parseFile', { | ||
filename: filename, | ||
src: src | ||
}); | ||
/** | ||
* Parse the given file and return all found dependencies. | ||
* @param {String} filename | ||
* @return {Array} | ||
*/ | ||
ES6.prototype.parseFile = function (filename) { | ||
try { | ||
if (fs.existsSync(filename)) { // eslint-disable-line no-sync | ||
var dependencies = []; | ||
var src = this.getFileSource(filename); | ||
var fileData = {filename: filename, src: src}; | ||
if (/import.*from/m.test(src) || /export.*from/m.test(src)) { | ||
detective(src).map((id) => { | ||
const depFilename = this.resolve(path.dirname(filename), id); | ||
this.emit('parseFile', fileData); | ||
if (depFilename) { | ||
return this.normalize(depFilename); | ||
} | ||
}).filter((id) => { | ||
if (!this.isExcluded(id) && dependencies.indexOf(id) < 0) { | ||
dependencies.push(id); | ||
} | ||
}); | ||
} | ||
if (/import.*from/m.test(fileData.src) || /export.*from/m.test(fileData.src)) { | ||
detective(fileData.src).map(function (id) { | ||
var depFilename = this.resolve(path.dirname(fileData.filename), id); | ||
if (depFilename) { | ||
return this.normalize(depFilename); | ||
} | ||
}, this).filter(function (id) { | ||
if (!this.isExcluded(id) && dependencies.indexOf(id) < 0) { | ||
dependencies.push(id); | ||
} | ||
}, this); | ||
return dependencies; | ||
} | ||
} | ||
} catch (e) { | ||
if (this.opts.breakOnError) { | ||
console.log(String('\nError while parsing file: ' + filename).red); | ||
throw e; | ||
} | ||
return dependencies; | ||
} | ||
} | ||
return []; | ||
}; | ||
module.exports = ES6; |
'use strict'; | ||
require('colors'); | ||
/** | ||
* Module dependencies. | ||
* Return colored string (or not). | ||
* @param {String} str | ||
* @param {String} name | ||
* @param {Boolean} use | ||
* @return {String} | ||
*/ | ||
var c = require('./color'); | ||
function c(str, name, use) { | ||
return use ? String(str)[name] : str; | ||
} | ||
@@ -30,8 +38,8 @@ /** | ||
Object.keys(modules).forEach(function (id) { | ||
Object.keys(modules).forEach((id) => { | ||
console.log(c(id, 'cyan', opts.colors)); | ||
modules[id].forEach(function (depId) { | ||
modules[id].forEach((depId) => { | ||
console.log(c(' ' + depId, 'grey', opts.colors)); | ||
}, this); | ||
}, this); | ||
}); | ||
}); | ||
}; | ||
@@ -46,9 +54,9 @@ | ||
module.exports.summary = function (modules, opts) { | ||
var o = {}; | ||
const o = {}; | ||
opts = opts || {}; | ||
Object.keys(modules).sort(function (a, b) { | ||
Object.keys(modules).sort((a, b) => { | ||
return modules[b].length - modules[a].length; | ||
}).forEach(function (id) { | ||
}).forEach((id) => { | ||
if (opts.output === 'json') { | ||
@@ -73,3 +81,4 @@ o[id] = modules[id].length; | ||
module.exports.circular = function (circular, opts) { | ||
var arr = circular.getArray(); | ||
const arr = circular.getArray(); | ||
if (opts.output === 'json') { | ||
@@ -82,4 +91,4 @@ return process.stdout.write(toJSON(arr)); | ||
} else { | ||
arr.forEach(function (path, idx) { | ||
path.forEach(function (module, idx) { | ||
arr.forEach((path, idx) => { | ||
path.forEach((module, idx) => { | ||
if (idx) { | ||
@@ -107,5 +116,5 @@ process.stdout.write(c(' -> ', 'cyan', opts.colors)); | ||
modules.forEach(function (id) { | ||
modules.forEach((id) => { | ||
console.log(c(id, 'grey', opts.colors)); | ||
}); | ||
}; |
{ | ||
"name": "madge", | ||
"version": "0.5.5", | ||
"version": "0.6.0", | ||
"author": "Patrik Henningsson <patrik.henningsson@gmail.com>", | ||
@@ -22,10 +22,10 @@ "repository": "git://github.com/pahen/madge", | ||
], | ||
"engines": [ | ||
"node >= 0.8.0" | ||
], | ||
"engines": { | ||
"node": ">=4.x.x" | ||
}, | ||
"scripts": { | ||
"test": "npm run lint && npm run mocha && npm run madge", | ||
"mocha": "mocha test/*.js", | ||
"lint": "eslint bin lib", | ||
"madge": "bin/madge -c -f cjs ./lib", | ||
"lint": "eslint bin/madge lib test/*.js", | ||
"madge": "bin/madge -c -L ./lib", | ||
"release": "npm test && release-it -n -i patch", | ||
@@ -32,0 +32,0 @@ "release:minor": "npm run test && release-it -n -i minor", |
@@ -142,2 +142,3 @@ # MaDGe - Module Dependency Graph | ||
-s, --summary show summary of all dependencies | ||
-L, --list show list of all dependencies | ||
-c, --circular show circular dependencies | ||
@@ -256,8 +257,11 @@ -d, --depends <id> show modules that depends on the given id | ||
## v0.6.0 (July 06, 2016) | ||
* Refactored Madge to use ES6 and now requires Node.js 4 to run. | ||
## v0.5.5 (July 03, 2016) | ||
* Add note about Graphviz and Windows in README | ||
* Fix matching absolute path in Windows (Thanks to nadejdashed) | ||
* Support for ES6 re-export syntax (Thanks to Oli Lalonde) | ||
* Support files with ES6 (Thanks to Joel Kemp) | ||
* Improve readme circular return object (Thanks to Way Of The Future) | ||
* Add note about Graphviz and Windows in README. | ||
* Fix matching absolute path in Windows (Thanks to nadejdashed). | ||
* Support for ES6 re-export syntax (Thanks to Oli Lalonde). | ||
* Support files with ES6 (Thanks to Joel Kemp). | ||
* Improve readme circular return object (Thanks to Way Of The Future). | ||
@@ -264,0 +268,0 @@ ## v0.5.4 (June 13, 2016) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
129157
383
3
1733