From 4be7db2f096c6695ac1b8eacfd0095c5fa7ac7fb Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Thu, 21 Jul 2016 08:41:48 +0200 Subject: [PATCH 01/39] Replace parsers with dependency-tree module --- lib/parse/amd.js | 263 ------ lib/parse/base.js | 188 ---- lib/parse/cjs.js | 53 -- lib/parse/es6.js | 40 - lib/parse/parse.js | 851 ------------------- npm-shrinkwrap.json | 132 --- package.json | 28 +- test/amd.js | 165 ++-- test/cjs.js | 83 +- test/es6.js | 94 +- test/files/amd/amdes6.js | 2 +- test/files/amd/circular/main.js | 3 + test/files/amd/circularRelative/a.coffee | 1 - test/files/amd/circularRelative/a.js | 3 + test/files/amd/circularRelative/foo/b.coffee | 1 - test/files/amd/circularRelative/foo/b.js | 3 + test/files/amd/plugin.js | 2 +- test/files/amd/relative/a.js | 3 - test/files/amd/relative/b.js | 3 - test/files/amd/relative/foo/bar/d.js | 3 - test/files/amd/relative/foo/c.js | 3 - test/files/cjs/core.js | 2 + test/files/cjs/npm.js | 2 + test/files/es6/absolute.js | 2 +- test/files/es6/async.js | 2 +- test/files/es6/jsx.js | 2 +- test/madge.js | 63 +- test/pluggable.js | 57 -- 28 files changed, 205 insertions(+), 1849 deletions(-) delete mode 100644 lib/parse/amd.js delete mode 100644 lib/parse/base.js delete mode 100644 lib/parse/cjs.js delete mode 100644 lib/parse/es6.js delete mode 100644 lib/parse/parse.js delete mode 100644 npm-shrinkwrap.json create mode 100644 test/files/amd/circular/main.js delete mode 100644 test/files/amd/circularRelative/a.coffee create mode 100644 test/files/amd/circularRelative/a.js delete mode 100644 test/files/amd/circularRelative/foo/b.coffee create mode 100644 test/files/amd/circularRelative/foo/b.js delete mode 100644 test/files/amd/relative/a.js delete mode 100644 test/files/amd/relative/b.js delete mode 100644 test/files/amd/relative/foo/bar/d.js delete mode 100644 test/files/amd/relative/foo/c.js create mode 100644 test/files/cjs/core.js create mode 100644 test/files/cjs/npm.js delete mode 100644 test/pluggable.js diff --git a/lib/parse/amd.js b/lib/parse/amd.js deleted file mode 100644 index 5821691c..00000000 --- a/lib/parse/amd.js +++ /dev/null @@ -1,263 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const amdetective = require('amdetective'); -const parse = require('./parse'); -const Base = require('./base'); - -/** - * Merge the two given trees. - * @param {Object} a - * @param {Object} b - */ -function mergeTrees(a, b) { - Object.keys(b).forEach((id) => { - if (!a[id]) { - a[id] = []; - } - - b[id].forEach((dep) => { - if (a[id].indexOf(dep) < 0) { - a[id].push(dep); - } - }); - }); -} - -/** - * Helper for re-mapping path-refs to id-refs that are specified in RequireJS' path config. - * @param {Object} deps (dependency-list) - * @param {Object} pathDefs (path-definitions from requirejs-config) - * @param {String} baseDir (base directory of source files) - */ -function convertPathsToIds(deps, pathDefs, baseDir) { - let path, pathDeps, i1, len1, i2, len2; - - if (baseDir) { - baseDir += '/'; - } else { - baseDir = ''; - } - - Object.keys(pathDefs).forEach((id) => { - path = pathDefs[id]; - - // if path does not start with / or a protocol: prepend with baseDir - if (!/^[^\/]+:\/\/|^\//m.test(path)) { - path = baseDir + path; - } - - if (path !== id && deps[path]) { - if (deps[id] && deps[id].length > 0) { - pathDeps = deps[path].slice(0, deps[path].length - 1); - - // remove entries from , if already contained in - for (i1 = 0, len1 = pathDeps.length; i1 < len1; ++i1) { - for (i2 = 0, len2 = deps[id].length; i2 < len2; ++i2) { - if (pathDeps[i1] === deps[id][i2]) { - pathDeps.splice(i1--, 1); - break; - } - } - } - deps[id] = deps[id].concat(pathDeps); - } else { - deps[id] = deps[path]; - } - - delete deps[path]; - } else if (!deps[id]) { - deps[id] = []; - } - - // normalize entries within deps-arrays (i.e. replace path-refs with id-refs) - Object.keys(pathDefs).forEach((id) => { - path = baseDir + pathDefs[id]; - if (deps[id]) { - for (i1 = 0, len1 = deps[id].length; i1 < len1; ++i1) { - // replace path-ref with id-ref (if necessary) - if (deps[id][i1] === path) { - deps[id][i1] = id; - } - } - } - }); - }); -} - -/** - * Read shim dependencies from RequireJS config. - * @param {String} filename - * @param {String} [exclude] - * @return {Object} - */ -function getShimDepsFromConfig(filename, exclude) { - 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)); - }; - - if (config.shim) { - Object.keys(config.shim).filter(isIncluded).forEach((key) => { - if (config.shim[key].deps) { - deps[key] = config.shim[key].deps.filter(isIncluded); - } else { - deps[key] = []; - } - }); - } - - return deps; -} - -/** -* Read path definitions from RequireJS config. -* @param {String} filename -* @param {String} [exclude] -* @return {Object} -*/ -function getPathsFromConfig(filename, exclude) { - 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)); - }; - - if (config.paths) { - Object.keys(config.paths).filter(isIncluded).forEach((key) => { - paths[key] = config.paths[key]; - }); - } - - return paths; -} - -/** - * Read baseUrl from RequireJS config. - * @param {String} filename - * @param {String} srcBaseDir - * @return {String} - */ -function getBaseUrlFromConfig(filename, srcBaseDir) { - const config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); // eslint-disable-line no-sync - return config.baseUrl ? path.relative(srcBaseDir, config.baseUrl) : ''; -} - -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) { - 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)); - } - } - - /** - * 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, '')); - } - - /** - * 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', { - filename: filename, - src: src - }); - - if (/define|require\s*\(/m.test(src)) { - amdetective(src, {findNestedDependencies: this.opts.findNestedDependencies}).map((obj) => { - return typeof obj === 'string' ? [obj] : obj.deps; - }).filter((deps) => { - deps.filter((id) => { - // Ignore RequireJS IDs and plugins - return id !== 'require' && id !== 'exports' && id !== 'module' && !id.match(/\.?\w\!/); - }).map((id) => { - // Only resolve relative module identifiers (if the first term is "." or "..") - if (id.charAt(0) !== '.') { - return id; - } - - const depFilename = path.resolve(path.dirname(filename), id); - - if (depFilename) { - return this.normalize(depFilename); - } - }).forEach((id) => { - if (!this.isExcluded(id) && dependencies.indexOf(id) < 0) { - dependencies.push(id); - } - }); - }); - } - - return dependencies; - } - - /** - * Get module dependencies from optimize file (r.js). - * @param {String} filename - */ - addOptimizedModules(filename) { - const 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; - } - - 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 (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); - } - } -} - -module.exports = AMD; diff --git a/lib/parse/base.js b/lib/parse/base.js deleted file mode 100644 index 8aaf842d..00000000 --- a/lib/parse/base.js +++ /dev/null @@ -1,188 +0,0 @@ -'use strict'; - -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'); - -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.onParseFile) { - this.on('parseFile', opts.onParseFile.bind(parent)); - } - - if (opts.onAddModule) { - this.on('addModule', opts.onAddModule.bind(parent)); - } - - this.opts = opts; - - if (typeof this.opts.extensions === 'undefined') { - this.opts.extensions = ['.js']; - } - - 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} - */ - 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; - } - } - - /** - * 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 - return path.dirname(dir); - } - - return dir; - } - - /** - * 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} - */ - normalize(filename) { - return this.replaceBackslashInPath(path.relative(this.baseDir, filename).replace(this.extRegEx, '')); - } - - /** - * 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 - */ - addModule(filename) { - const id = this.normalize(filename); - - 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 - */ - 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 - }); - } - - return src; - } - - /** - * Sort dependencies by name. - */ - sortDependencies() { - this.tree = Object.keys(this.tree).sort().reduce((acc, id) => { - (acc[id] = this.tree[id]).sort(); - return acc; - }, {}); - } - - /** - * Replace back slashes in path (Windows) with forward slashes (*nix). - * @param {String} path - * @return {String} - */ - replaceBackslashInPath(path) { - return path.replace(/\\/g, '/'); - } -} - -module.exports = Base; diff --git a/lib/parse/cjs.js b/lib/parse/cjs.js deleted file mode 100644 index 4635f095..00000000 --- a/lib/parse/cjs.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -const path = require('path'); -const detective = require('detective'); -const Base = require('./base'); - -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); - } - - /** - * 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', { - filename: filename, - src: src - }); - - 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; - } -} - -module.exports = CJS; diff --git a/lib/parse/es6.js b/lib/parse/es6.js deleted file mode 100644 index bb6e27cf..00000000 --- a/lib/parse/es6.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -const path = require('path'); -const detective = require('detective-es6'); -const Base = require('./base'); - -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); - - this.emit('parseFile', { - 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); - - if (depFilename) { - return this.normalize(depFilename); - } - }).filter((id) => { - if (!this.isExcluded(id) && dependencies.indexOf(id) < 0) { - dependencies.push(id); - } - }); - } - - return dependencies; - } -} - -module.exports = ES6; diff --git a/lib/parse/parse.js b/lib/parse/parse.js deleted file mode 100644 index 0201b40b..00000000 --- a/lib/parse/parse.js +++ /dev/null @@ -1,851 +0,0 @@ -'use strict'; - -/** - * Copied from https://github.com/jrburke/r.js/blob/master/build/jslib/parse.js with a couple - * of changes to make it run in node. - */ - -/** - * @license Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ - -/* eslint-disable */ - -var uglify = require('uglify-js'), - parser = uglify.parser, - processor = uglify.uglify, - ostring = Object.prototype.toString, - isArray; - -if (Array.isArray) { - isArray = Array.isArray; -} else { - isArray = function (it) { - return ostring.call(it) === "[object Array]"; - }; -} - -/** - * Determines if the AST node is an array literal - */ -function isArrayLiteral(node) { - return node[0] === 'array'; -} - -/** - * Determines if the AST node is an object literal - */ -function isObjectLiteral(node) { - return node[0] === 'object'; -} - -/** - * Converts a regular JS array of strings to an AST node that - * represents that array. - * @param {Array} ary - * @param {Node} an AST node that represents an array of strings. - */ -function toAstArray(ary) { - var output = [ - 'array', - [] - ], - i, item; - - for (i = 0; (item = ary[i]); i++) { - output[1].push([ - 'string', - item - ]); - } - - return output; -} - -/** - * Validates a node as being an object literal (like for i18n bundles) - * or an array literal with just string members. If an array literal, - * only return array members that are full strings. So the caller of - * this function should use the return value as the new value for the - * node. - * - * This function does not need to worry about comments, they are not - * present in this AST. - * - * @param {Node} node an AST node. - * - * @returns {Node} an AST node to use for the valid dependencies. - * If null is returned, then it means the input node was not a valid - * dependency. - */ -function validateDeps(node) { - var newDeps = ['array', []], - arrayArgs, i, dep; - - if (!node) { - return null; - } - - if (isObjectLiteral(node) || node[0] === 'function') { - return node; - } - - //Dependencies can be an object literal or an array. - if (!isArrayLiteral(node)) { - return null; - } - - arrayArgs = node[1]; - - for (i = 0; i < arrayArgs.length; i++) { - dep = arrayArgs[i]; - if (dep[0] === 'string') { - newDeps[1].push(dep); - } - } - return newDeps[1].length ? newDeps : null; -} - -/** - * Gets dependencies from a node, but only if it is an array literal, - * and only if the dependency is a string literal. - * - * This function does not need to worry about comments, they are not - * present in this AST. - * - * @param {Node} node an AST node. - * - * @returns {Array} of valid dependencies. - * If null is returned, then it means the input node was not a valid - * array literal, or did not have any string literals.. - */ -function getValidDeps(node) { - var newDeps = [], - arrayArgs, i, dep; - - if (!node) { - return null; - } - - if (isObjectLiteral(node) || node[0] === 'function') { - return null; - } - - //Dependencies can be an object literal or an array. - if (!isArrayLiteral(node)) { - return null; - } - - arrayArgs = node[1]; - - for (i = 0; i < arrayArgs.length; i++) { - dep = arrayArgs[i]; - if (dep[0] === 'string') { - newDeps.push(dep[1]); - } - } - return newDeps.length ? newDeps : null; -} - -/** - * Main parse function. Returns a string of any valid require or define/require.def - * calls as part of one JavaScript source string. - * @param {String} moduleName the module name that represents this file. - * It is used to create a default define if there is not one already for the file. - * This allows properly tracing dependencies for builds. Otherwise, if - * the file just has a require() call, the file dependencies will not be - * properly reflected: the file will come before its dependencies. - * @param {String} moduleName - * @param {String} fileName - * @param {String} fileContents - * @param {Object} options optional options. insertNeedsDefine: true will - * add calls to require.needsDefine() if appropriate. - * @returns {String} JS source string or null, if no require or define/require.def - * calls are found. - */ -function parse(moduleName, fileName, fileContents, options) { - options = options || {}; - - //Set up source input - var moduleDeps = [], - result = '', - moduleList = [], - needsDefine = true, - astRoot = parser.parse(fileContents), - i, moduleCall, depString; - - parse.recurse(astRoot, function (callName, config, name, deps) { - //If name is an array, it means it is an anonymous module, - //so adjust args appropriately. An anonymous module could - //have a FUNCTION as the name type, but just ignore those - //since we just want to find dependencies. - if (name && isArrayLiteral(name)) { - deps = name; - name = null; - } - - if (!(deps = getValidDeps(deps))) { - deps = []; - } - - //Get the name as a string literal, if it is available. - if (name && name[0] === 'string') { - name = name[1]; - } else { - name = null; - } - - if (callName === 'define' && (!name || name === moduleName)) { - needsDefine = false; - } - - if (!name) { - //If there is no module name, the dependencies are for - //this file/default module name. - moduleDeps = moduleDeps.concat(deps); - } else { - moduleList.push({ - name: name, - deps: deps - }); - } - - //If define was found, no need to dive deeper, unless - //the config explicitly wants to dig deeper. - return !options.findNestedDependencies; - }, options); - - if (options.insertNeedsDefine && needsDefine) { - result += 'require.needsDefine("' + moduleName + '");'; - } - - if (moduleDeps.length || moduleList.length) { - for (i = 0; (moduleCall = moduleList[i]); i++) { - if (result) { - result += '\n'; - } - - //If this is the main module for this file, combine any - //"anonymous" dependencies (could come from a nested require - //call) with this module. - if (moduleCall.name === moduleName) { - moduleCall.deps = moduleCall.deps.concat(moduleDeps); - moduleDeps = []; - } - - depString = moduleCall.deps.length ? '["' + moduleCall.deps.join('","') + '"]' : '[]'; - result += 'define("' + moduleCall.name + '",' + depString + ');'; - } - if (moduleDeps.length) { - if (result) { - result += '\n'; - } - depString = moduleDeps.length ? '["' + moduleDeps.join('","') + '"]' : '[]'; - result += 'define("' + moduleName + '",' + depString + ');'; - } - } - - return result ? result : null; -} - -//Add some private methods to object for use in derived objects. -parse.isArray = isArray; -parse.isObjectLiteral = isObjectLiteral; -parse.isArrayLiteral = isArrayLiteral; - -/** - * Handles parsing a file recursively for require calls. - * @param {Array} parentNode the AST node to start with. - * @param {Function} onMatch function to call on a parse match. - * @param {Object} [options] This is normally the build config options if - * it is passed. - * @param {Function} [recurseCallback] function to call on each valid - * node, defaults to parse.parseNode. - */ -parse.recurse = function (parentNode, onMatch, options, recurseCallback) { - var hasHas = options && options.has, - i, node; - - recurseCallback = recurseCallback || this.parseNode; - - if (isArray(parentNode)) { - for (i = 0; i < parentNode.length; i++) { - node = parentNode[i]; - if (isArray(node)) { - //If has config is in play, if calls have been converted - //by this point to be true/false values. So, if - //options has a 'has' value, skip if branches that have - //literal false values. - - //uglify returns if constructs in an array: - //[0]: 'if' - //[1]: the condition, ['name', true | false] for the has replaced case. - //[2]: the block to process if true - //[3]: the block to process if false - //For if/else if/else, the else if is in the [3], - //so only ever have to deal with this structure. - if (hasHas && node[0] === 'if' && node[1] && node[1][0] === 'name' && - (node[1][1] === 'true' || node[1][1] === 'false')) { - if (node[1][1] === 'true') { - this.recurse([node[2]], onMatch, options, recurseCallback); - } else { - this.recurse([node[3]], onMatch, options, recurseCallback); - } - } else { - if (recurseCallback(node, onMatch)) { - //The onMatch indicated parsing should - //stop for children of this node. - continue; - } - this.recurse(node, onMatch, options, recurseCallback); - } - } - } - } -}; - -/** - * Determines if the file defines require(). - * @param {String} fileName - * @param {String} fileContents - * @returns {Boolean} - */ -parse.definesRequire = function (fileName, fileContents) { - var astRoot = parser.parse(fileContents); - return this.nodeHasRequire(astRoot); -}; - -/** - * Finds require("") calls inside a CommonJS anonymous module wrapped in a - * define(function(require, exports, module){}) wrapper. These dependencies - * will be added to a modified define() call that lists the dependencies - * on the outside of the function. - * @param {String} fileName - * @param {String} fileContents - * @returns {Array} an array of module names that are dependencies. Always - * returns an array, but could be of length zero. - */ -parse.getAnonDeps = function (fileName, fileContents) { - var astRoot = parser.parse(fileContents), - defFunc = this.findAnonDefineFactory(astRoot); - - return parse.getAnonDepsFromNode(defFunc); -}; - -/** - * Finds require("") calls inside a CommonJS anonymous module wrapped - * in a define function, given an AST node for the definition function. - * @param {Node} node the AST node for the definition function. - * @returns {Array} and array of dependency names. Can be of zero length. - */ -parse.getAnonDepsFromNode = function (node) { - var deps = [], - funcArgLength; - - if (node) { - this.findRequireDepNames(node, deps); - - //If no deps, still add the standard CommonJS require, exports, module, - //in that order, to the deps, but only if specified as function args. - //In particular, if exports is used, it is favored over the return - //value of the function, so only add it if asked. - funcArgLength = node[2] && node[2].length; - if (funcArgLength) { - deps = (funcArgLength > 1 ? ["require", "exports", "module"] : - ["require"]).concat(deps); - } - } - return deps; -}; - -/** - * Finds the function in define(function (require, exports, module){}); - * @param {Array} node - * @returns {Boolean} - */ -parse.findAnonDefineFactory = function (node) { - var callback, i, n, call, args; - - if (isArray(node)) { - if (node[0] === 'call') { - call = node[1]; - args = node[2]; - if ((call[0] === 'name' && call[1] === 'define') || - (call[0] === 'dot' && call[1][1] === 'require' && call[2] === 'def')) { - - //There should only be one argument and it should be a function, - //or a named module with function as second arg - if (args.length === 1 && args[0][0] === 'function') { - return args[0]; - } else if (args.length === 2 && args[0][0] === 'string' && - args[1][0] === 'function') { - return args[1]; - } - } - } - - //Check child nodes - for (i = 0; i < node.length; i++) { - n = node[i]; - if ((callback = this.findAnonDefineFactory(n))) { - return callback; - } - } - } - - return null; -}; - -/** - * Finds any config that is passed to requirejs. - * @param {String} fileName - * @param {String} fileContents - * - * @returns {Object} a config object. Will be null if no config. - * Can throw an error if the config in the file cannot be evaluated in - * a build context to valid JavaScript. - */ -parse.findConfig = function (fileName, fileContents) { - /*jslint evil: true */ - //This is a litle bit inefficient, it ends up with two uglifyjs parser - //calls. Can revisit later, but trying to build out larger functional - //pieces first. - var foundConfig = null, - astRoot = parser.parse(fileContents); - - parse.recurse(astRoot, function (configNode) { - var jsConfig; - - if (!foundConfig && configNode) { - jsConfig = parse.nodeToString(configNode); - foundConfig = eval('(' + jsConfig + ')'); - return foundConfig; - } - return undefined; - }, null, parse.parseConfigNode); - - return foundConfig; -}; - -/** - * Finds all dependencies specified in dependency arrays and inside - * simplified commonjs wrappers. - * @param {String} fileName - * @param {String} fileContents - * - * @returns {Array} an array of dependency strings. The dependencies - * have not been normalized, they may be relative IDs. - */ -parse.findDependencies = function (fileName, fileContents, options) { - //This is a litle bit inefficient, it ends up with two uglifyjs parser - //calls. Can revisit later, but trying to build out larger functional - //pieces first. - var dependencies = [], - astRoot = parser.parse(fileContents); - - parse.recurse(astRoot, function (callName, config, name, deps) { - //Normalize the input args. - if (name && isArrayLiteral(name)) { - deps = name; - name = null; - } - - if ((deps = getValidDeps(deps))) { - dependencies = dependencies.concat(deps); - } - }, options); - - return dependencies; -}; - -/** - * Finds only CJS dependencies, ones that are the form require('stringLiteral') - */ -parse.findCjsDependencies = function (fileName, fileContents, options) { - //This is a litle bit inefficient, it ends up with two uglifyjs parser - //calls. Can revisit later, but trying to build out larger functional - //pieces first. - var dependencies = [], - astRoot = parser.parse(fileContents); - - parse.recurse(astRoot, function (dep) { - dependencies.push(dep); - }, options, function (node, onMatch) { - - var call, args; - - if (!isArray(node)) { - return false; - } - - if (node[0] === 'call') { - call = node[1]; - args = node[2]; - - if (call) { - //A require('') use. - if (call[0] === 'name' && call[1] === 'require' && - args[0][0] === 'string') { - return onMatch(args[0][1]); - } - } - } - - return false; - - }); - - return dependencies; -}; - -/** - * Determines if define(), require({}|[]) or requirejs was called in the - * file. Also finds out if define() is declared and if define.amd is called. - */ -parse.usesAmdOrRequireJs = function (fileName, fileContents, options) { - var astRoot = parser.parse(fileContents), - uses; - - parse.recurse(astRoot, function (prop) { - if (!uses) { - uses = {}; - } - uses[prop] = true; - }, options, parse.findAmdOrRequireJsNode); - - return uses; -}; - -/** - * Determines if require(''), exports.x =, module.exports =, - * __dirname, __filename are used. So, not strictly traditional CommonJS, - * also checks for Node variants. - */ -parse.usesCommonJs = function (fileName, fileContents, options) { - var uses = null, - assignsExports = false, - astRoot = parser.parse(fileContents); - - parse.recurse(astRoot, function (prop) { - if (prop === 'varExports') { - assignsExports = true; - } else if (prop !== 'exports' || !assignsExports) { - if (!uses) { - uses = {}; - } - uses[prop] = true; - } - }, options, function (node, onMatch) { - - var call, args; - - if (!isArray(node)) { - return false; - } - - if (node[0] === 'name' && (node[1] === '__dirname' || node[1] === '__filename')) { - return onMatch(node[1].substring(2)); - } else if (node[0] === 'var' && node[1] && node[1][0] && node[1][0][0] === 'exports') { - //Hmm, a variable assignment for exports, so does not use cjs exports. - return onMatch('varExports'); - } else if (node[0] === 'assign' && node[2] && node[2][0] === 'dot') { - args = node[2][1]; - - if (args) { - //An exports or module.exports assignment. - if (args[0] === 'name' && args[1] === 'module' && - node[2][2] === 'exports') { - return onMatch('moduleExports'); - } else if (args[0] === 'name' && args[1] === 'exports') { - return onMatch('exports'); - } - } - } else if (node[0] === 'call') { - call = node[1]; - args = node[2]; - - if (call) { - //A require('') use. - if (call[0] === 'name' && call[1] === 'require' && - args[0][0] === 'string') { - return onMatch('require'); - } - } - } - - return false; - - }); - - return uses; -}; - - -parse.findRequireDepNames = function (node, deps) { - var moduleName, i, n, call, args; - - if (isArray(node)) { - if (node[0] === 'call') { - call = node[1]; - args = node[2]; - - if (call && call[0] === 'name' && call[1] === 'require') { - moduleName = args[0]; - if (moduleName[0] === 'string') { - deps.push(moduleName[1]); - } - } - - - } - - //Check child nodes - for (i = 0; i < node.length; i++) { - n = node[i]; - this.findRequireDepNames(n, deps); - } - } -}; - -/** - * Determines if a given node contains a require() definition. - * @param {Array} node - * @returns {Boolean} - */ -parse.nodeHasRequire = function (node) { - if (this.isDefineNode(node)) { - return true; - } - - if (isArray(node)) { - for (var i = 0, n; i < node.length; i++) { - n = node[i]; - if (this.nodeHasRequire(n)) { - return true; - } - } - } - - return false; -}; - -/** - * Is the given node the actual definition of define(). Actually uses - * the definition of define.amd to find require. - * @param {Array} node - * @returns {Boolean} - */ -parse.isDefineNode = function (node) { - //Actually look for the define.amd = assignment, since - //that is more indicative of RequireJS vs a plain require definition. - var assign; - if (!node) { - return null; - } - - if (node[0] === 'assign' && node[1] === true) { - assign = node[2]; - if (assign[0] === 'dot' && assign[1][0] === 'name' && - assign[1][1] === 'define' && assign[2] === 'amd') { - return true; - } - } - return false; -}; - -/** - * Determines if a specific node is a valid require or define/require.def call. - * @param {Array} node - * @param {Function} onMatch a function to call when a match is found. - * It is passed the match name, and the config, name, deps possible args. - * The config, name and deps args are not normalized. - * - * @returns {String} a JS source string with the valid require/define call. - * Otherwise null. - */ -parse.parseNode = function (node, onMatch) { - var call, name, config, deps, args, cjsDeps; - - if (!isArray(node)) { - return false; - } - - if (node[0] === 'call') { - call = node[1]; - args = node[2]; - - if (call) { - if (call[0] === 'name' && - (call[1] === 'require' || call[1] === 'requirejs')) { - - //It is a plain require() call. - config = args[0]; - deps = args[1]; - if (isArrayLiteral(config)) { - deps = config; - config = null; - } - - if (!(deps = validateDeps(deps))) { - return null; - } - - return onMatch("require", null, null, deps); - - } else if (call[0] === 'name' && call[1] === 'define') { - - //A define call - name = args[0]; - deps = args[1]; - //Only allow define calls that match what is expected - //in an AMD call: - //* first arg should be string, array, function or object - //* second arg optional, or array, function or object. - //This helps weed out calls to a non-AMD define, but it is - //not completely robust. Someone could create a define - //function that still matches this shape, but this is the - //best that is possible, and at least allows UglifyJS, - //which does create its own internal define in one file, - //to be inlined. - if (((name[0] === 'string' || isArrayLiteral(name) || - name[0] === 'function' || isObjectLiteral(name))) && - (!deps || isArrayLiteral(deps) || - deps[0] === 'function' || isObjectLiteral(deps) || - // allow define(['dep'], factory) pattern - (isArrayLiteral(name) && deps[0] === 'name' && args.length === 2))) { - - //If first arg is a function, could be a commonjs wrapper, - //look inside for commonjs dependencies. - //Also, if deps is a function look for commonjs deps. - if (name && name[0] === 'function') { - cjsDeps = parse.getAnonDepsFromNode(name); - if (cjsDeps.length) { - name = toAstArray(cjsDeps); - } - } else if (deps && deps[0] === 'function') { - cjsDeps = parse.getAnonDepsFromNode(deps); - if (cjsDeps.length) { - deps = toAstArray(cjsDeps); - } - } - - return onMatch("define", null, name, deps); - } - } - } - } - - return false; -}; - -/** - * Looks for define(), require({} || []), requirejs({} || []) calls. - */ -parse.findAmdOrRequireJsNode = function (node, onMatch) { - var call, args, configNode, type; - - if (!isArray(node)) { - return false; - } - - if (node[0] === 'defun' && node[1] === 'define') { - type = 'declaresDefine'; - } else if (node[0] === 'assign' && node[2] && node[2][2] === 'amd' && - node[2][1] && node[2][1][0] === 'name' && - node[2][1][1] === 'define') { - type = 'defineAmd'; - } else if (node[0] === 'call') { - call = node[1]; - args = node[2]; - - if (call) { - if ((call[0] === 'dot' && - (call[1] && call[1][0] === 'name' && - (call[1][1] === 'require' || call[1][1] === 'requirejs')) && - call[2] === 'config')) { - //A require.config() or requirejs.config() call. - type = call[1][1] + 'Config'; - } else if (call[0] === 'name' && - (call[1] === 'require' || call[1] === 'requirejs')) { - //A require() or requirejs() config call. - //Only want ones that start with an object or an array. - configNode = args[0]; - if (configNode[0] === 'object' || configNode[0] === 'array') { - type = call[1]; - } - } else if (call[0] === 'name' && call[1] === 'define') { - //A define call. - type = 'define'; - } - } - } - - if (type) { - return onMatch(type); - } - - return false; -}; - -/** - * Determines if a specific node is a valid require/requirejs config - * call. That includes calls to require/requirejs.config(). - * @param {Array} node - * @param {Function} onMatch a function to call when a match is found. - * It is passed the match name, and the config, name, deps possible args. - * The config, name and deps args are not normalized. - * - * @returns {String} a JS source string with the valid require/define call. - * Otherwise null. - */ -parse.parseConfigNode = function (node, onMatch) { - var call, configNode, args; - - if (!isArray(node)) { - return false; - } - - if (node[0] === 'call') { - call = node[1]; - args = node[2]; - - if (call) { - //A require.config() or requirejs.config() call. - if ((call[0] === 'dot' && - (call[1] && call[1][0] === 'name' && - (call[1][1] === 'require' || call[1][1] === 'requirejs')) && - call[2] === 'config') || - //A require() or requirejs() config call. - - (call[0] === 'name' && - (call[1] === 'require' || call[1] === 'requirejs')) - ) { - //It is a plain require() call. - configNode = args[0]; - - if (configNode[0] !== 'object') { - return null; - } - - return onMatch(configNode); - - } - } - } - - return false; -}; - -/** - * Converts an AST node into a JS source string. Does not maintain formatting - * or even comments from original source, just returns valid JS source. - * @param {Array} node - * @returns {String} a JS source string. - */ -parse.nodeToString = function (node) { - return processor.gen_code(node, true); -}; - -module.exports = parse; \ No newline at end of file diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json deleted file mode 100644 index 200738e5..00000000 --- a/npm-shrinkwrap.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "name": "madge", - "version": "0.5.4", - "dependencies": { - "amdetective": { - "version": "0.2.1", - "from": "amdetective@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/amdetective/-/amdetective-0.2.1.tgz", - "dependencies": { - "esprima": { - "version": "2.7.2", - "from": "esprima@>=2.7.0 <2.8.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.2.tgz" - } - } - }, - "coffee-script": { - "version": "1.10.0", - "from": "coffee-script@1.10.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz" - }, - "colors": { - "version": "1.1.2", - "from": "colors@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz" - }, - "commander": { - "version": "2.9.0", - "from": "commander@>=2.9.0 <3.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "dependencies": { - "graceful-readlink": { - "version": "1.0.1", - "from": "graceful-readlink@>=1.0.0", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" - } - } - }, - "commondir": { - "version": "1.0.1", - "from": "commondir@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" - }, - "detective": { - "version": "4.3.1", - "from": "detective@>=4.3.1 <5.0.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.3.1.tgz", - "dependencies": { - "acorn": { - "version": "1.2.2", - "from": "acorn@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz" - }, - "defined": { - "version": "1.0.0", - "from": "defined@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz" - } - } - }, - "detective-es6": { - "version": "1.1.5", - "from": "detective-es6@1.1.5", - "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-1.1.5.tgz", - "dependencies": { - "node-source-walk": { - "version": "3.0.0", - "from": "node-source-walk@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-3.0.0.tgz", - "dependencies": { - "babylon": { - "version": "6.8.2", - "from": "babylon@>=6.8.1 <6.9.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.8.2.tgz", - "dependencies": { - "babel-runtime": { - "version": "6.9.2", - "from": "babel-runtime@>=6.0.0 <7.0.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.9.2.tgz", - "dependencies": { - "core-js": { - "version": "2.4.0", - "from": "core-js@>=2.4.0 <3.0.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.0.tgz" - }, - "regenerator-runtime": { - "version": "0.9.5", - "from": "regenerator-runtime@>=0.9.5 <0.10.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz" - } - } - } - } - }, - "object-assign": { - "version": "4.1.0", - "from": "object-assign@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" - } - } - } - } - }, - "graphviz": { - "version": "0.0.8", - "from": "graphviz@0.0.8", - "resolved": "https://registry.npmjs.org/graphviz/-/graphviz-0.0.8.tgz", - "dependencies": { - "temp": { - "version": "0.4.0", - "from": "temp@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.4.0.tgz" - } - } - }, - "resolve": { - "version": "1.1.7", - "from": "resolve@>=1.1.7 <2.0.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" - }, - "uglify-js": { - "version": "1.3.5", - "from": "uglify-js@>=1.3.5 <2.0.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.3.5.tgz" - }, - "walkdir": { - "version": "0.0.11", - "from": "walkdir@0.0.11", - "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz" - } - } -} diff --git a/package.json b/package.json index 368a39ec..9caba727 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "repository": "git://github.com/pahen/madge", "homepage": "https://github.com/pahen/madge", "license": "MIT", - "description": "Create graphs from your CommonJS, AMD or ES6 module dependencies.", + "description": "Create graphs from module dependencies.", "keywords": [ "ES6", "ES7", @@ -23,36 +23,32 @@ "node": ">=4.x.x" }, "scripts": { - "test": "npm run lint && npm run mocha && npm run madge", + "test": "npm run lint && npm run madge && npm run mocha", "mocha": "mocha test/*.js", - "lint": "eslint bin/madge lib test/*.js", - "madge": "bin/madge -c -L ./lib", + "watch": "mocha --watch --growl test/*.js", + "lint": "eslint bin/cli.js lib test/*.js", + "madge": "bin/cli.js bin/cli.js", "release": "npm test && release-it -n -i patch", "release:minor": "npm run test && release-it -n -i minor", "release:major": "npm run test && release-it -n -i major" }, "dependencies": { - "amdetective": "~0.2.1", - "coffee-script": "1.10.0", "colors": "^1.1.2", "commander": "^2.9.0", - "commondir": "^1.0.1", - "detective": "^4.3.1", - "detective-es6": "1.1.5", - "graphviz": "0.0.8", - "resolve": "^1.1.7", - "uglify-js": "^1.3.5", - "walkdir": "0.0.11" + "debug": "^2.2.0", + "dependency-tree": "^5.4.1", + "graphviz": "^0.0.8", + "rc": "^1.1.6" }, "devDependencies": { "@aptoma/eslint-config": "^4.0.0", "eslint": "^3.0.0", "mocha": "^2.3.3", - "should": "*", - "release-it": "^2.4.0" + "release-it": "^2.4.0", + "should": "^9.0.2" }, "main": "./lib/madge", "bin": { - "madge": "./bin/madge" + "madge": "./bin/cli.js" } } diff --git a/test/amd.js b/test/amd.js index 965b1476..a51cb3ae 100644 --- a/test/amd.js +++ b/test/amd.js @@ -1,130 +1,101 @@ /* eslint-env mocha */ 'use strict'; -require('should'); const madge = require('../lib/madge'); +require('should'); -describe('module format (AMD)', () => { - - it('should behave as expected on ok files', () => { - 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', () => { - 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 handle optimized files originating with a `require` call', () => { - madge([__dirname + '/files/amd/b-built.js'], { - format: 'amd', - optimized: true - }).obj().should.eql({'': ['sub/b'], 'a': [], 'd': [], 'sub/b': ['sub/c'], 'sub/c': ['d']}); - }); - - it('should handle optimized files originating with a `require` call and a designated main module', () => { - madge([__dirname + '/files/amd/b-built.js'], { - format: 'amd', - optimized: true, - mainRequireModule: 'a' - }).obj().should.eql({'a': ['sub/b'], 'd': [], 'sub/b': ['sub/c'], 'sub/c': ['d']}); - }); - - it('should merge in shim dependencies found in RequireJS config', () => { - madge([__dirname + '/files/amd/requirejs/a.js'], { - format: 'amd', - requireConfig: __dirname + '/files/amd/requirejs/config.js' - }).obj().should.eql({'a': ['jquery'], 'jquery': [], 'jquery.foo': ['jquery'], 'jquery.bar': ['jquery'], 'baz': ['quux'], 'quux': []}); - }); - - it('should be able to exclude modules', () => { - madge([__dirname + '/files/amd/ok'], { - format: 'amd', - exclude: '^sub' - }).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': []}); +describe('AMD', () => { + const dir = __dirname + '/files/amd'; - madge([__dirname + '/files/amd/requirejs/a.js'], { - format: 'amd', - requireConfig: __dirname + '/files/amd/requirejs/config.js', - exclude: '^jquery.foo|quux$' - }).obj().should.eql({a: ['jquery'], 'jquery': [], 'jquery.bar': ['jquery'], 'baz': []}); + it('should find recursive dependencies', () => { + madge(dir + '/ok/a.js').obj().should.eql({ + 'a': ['sub/b'], + 'sub/b': ['sub/c'], + 'sub/c': ['d'], + 'd': [] + }); }); - it('should tackle errors in files', () => { - madge([__dirname + '/files/amd/error.js'], { - format: 'amd' - }).obj().should.eql({error: []}); + it('should ignore plugins', () => { + madge(dir + '/plugin.js').obj().should.eql({ + 'plugin': ['ok/d'], + 'ok/d': [] + }); }); - it('should handle named modules', () => { - madge([__dirname + '/files/amd/namedWrapped/car.js'], { - format: 'amd' - }).obj().should.eql({'car': ['engine', 'wheels']}); + it('should find nested dependencies', () => { + madge(dir + '/nested/main.js').obj().should.eql({ + 'a': [], + 'b': [], + 'main': [ + 'a', + 'b' + ] + }); }); it('should find circular dependencies', () => { - madge([__dirname + '/files/amd/circular'], { - format: 'amd' - }).circular().getArray().should.eql([['a', 'c'], ['f', 'g', 'h']]); + madge(dir + '/circular/main.js').circular().should.eql([ + ['a', 'c'], + ['f', 'g', 'h'] + ]); }); it('should find circular dependencies with relative paths', () => { - madge([__dirname + '/files/amd/circularRelative'], { - format: 'amd' - }).circular().getArray().should.eql([['a', 'foo/b']]); + madge(dir + '/circularRelative/a.js').circular().should.eql([['a', 'foo/b']]); }); it('should find circular dependencies with alias', () => { - madge([__dirname + '/files/amd/circularAlias'], { - format: 'amd', - requireConfig: __dirname + '/files/amd/circularAlias/config.js' - }).circular().getArray().should.eql([['cpu', 'jsdos']]); + madge(dir + '/circularAlias/dos.js', { + requireConfig: dir + '/circularAlias/config.js' + }).circular().should.eql([['dos', 'x86']]); }); - it('should find modules that depends on another', () => { - madge([__dirname + '/files/amd/ok'], { - format: 'amd' - }).depends('sub/c').should.eql(['e', 'sub/b']); + it('should work for files with ES6 code inside', () => { + madge(dir + '/amdes6.js') + .obj().should.eql({ + 'amdes6': ['ok/d'], + 'ok/d': [] + }); }); - it('should compile coffeescript on-the-fly', () => { - madge([__dirname + '/files/amd/coffeescript'], { - format: 'amd' - }).obj().should.eql({'a': ['b'], 'b': []}); + it('should use paths found in RequireJS config', () => { + madge(dir + '/requirejs/a.js', { + requireConfig: dir + '/requirejs/config.js' + }).obj().should.eql({ + 'a': ['vendor/jquery-2.0.3'], + 'vendor/jquery-2.0.3': [] + }); }); - it('should resolve relative module indentifiers', () => { - madge([__dirname + '/files/amd/relative'], { - format: 'amd' - }).obj().should.eql({'a': [], 'b': ['a'], 'foo/bar/d': ['a'], 'foo/c': ['a']}); + it.skip('should compile coffeescript on-the-fly', () => { + madge(dir + '/coffeescript/a.coffee').obj().should.eql({ + 'a': ['b'], 'b': [] + }); }); - it('should ignore plugins', () => { - madge([__dirname + '/files/amd/plugin.js'], { - format: 'amd', - breakOnError: true - }).obj().should.eql({plugin: ['ok/a']}); + it.skip('should handle optimized files', () => { + madge(dir + '/a-built.js').obj().should.eql( + {'a': ['sub/b'], 'd': [], 'sub/b': ['sub/c'], 'sub/c': ['d'] + }); }); - it('should find nested dependencies', () => { - madge([__dirname + '/files/amd/nested/main.js'], { - format: 'amd', - findNestedDependencies: true - }).obj().should.eql({'main': ['a', 'b']}); + it.skip('should handle optimized files originating with a `require` call', () => { + madge(dir + '/b-built.js').obj().should.eql({ + '': ['sub/b'], + 'a': [], + 'd': [], + 'sub/b': ['sub/c'], 'sub/c': ['d'] + }); }); - it('should work for amd files with es6 code inside', () => { - madge([__dirname + '/files/amd/amdes6.js'], { - format: 'amd' - }).obj().should.eql({'amdes6': ['ok/a']}); + it.skip('should handle optimized files originating with a `require` call and a designated main module', () => { + madge(dir + '/b-built.js', { + mainRequireModule: 'a' + }).obj().should.eql({ + 'a': ['sub/b'], + 'd': [], + 'sub/b': ['sub/c'], 'sub/c': ['d'] + }); }); }); diff --git a/test/cjs.js b/test/cjs.js index cbbc716f..3467c316 100644 --- a/test/cjs.js +++ b/test/cjs.js @@ -4,58 +4,63 @@ const madge = require('../lib/madge'); require('should'); -describe('module format (CommonJS)', () => { +describe('CommonJS', () => { + const dir = __dirname + '/files/cjs'; - it('should behave as expected on ok files', () => { - madge([__dirname + '/files/cjs/normal']) - .obj().should.eql({'a': ['sub/b'], 'fancy-main/not-index': [], 'd': [], 'sub/b': ['sub/c'], 'sub/c': ['d']}); + it('should find recursive dependencies', () => { + madge(dir + '/normal/a.js').obj().should.eql({ + 'a': ['sub/b'], + 'd': [], + 'sub/b': ['sub/c'], + 'sub/c': ['d'] + }); }); - it('should handle expressions in require call', () => { - madge([__dirname + '/files/cjs/both.js']) - .obj().should.eql({'both': ['node_modules/a', 'node_modules/b']}); + it('should handle paths outside directory', () => { + madge(dir + '/normal/sub/c.js').obj().should.eql({ + '../d': [], + 'c': ['../d'] + }); }); - it('should handle require call and chained functions', () => { - madge([__dirname + '/files/cjs/chained.js']) - .obj().should.eql({'chained': ['node_modules/a', 'node_modules/b', 'node_modules/c']}); - }); - - it('should handle nested require call', () => { - madge([__dirname + '/files/cjs/nested.js']) - .obj().should.eql({'nested': ['node_modules/a', 'node_modules/b', 'node_modules/c']}); + it('should find circular dependencies', () => { + madge(dir + '/circular/a.js').circular().should.eql([ + ['a', 'b', 'c'] + ]); }); - it('should handle strings in require call', () => { - madge([__dirname + '/files/cjs/strings.js']) - .obj().should.eql({strings: [ - 'events', 'node_modules/a', 'node_modules/b', 'node_modules/c', - 'node_modules/doom', 'node_modules/events2', 'node_modules/y' - ]}); + it('should exclude core modules by default', () => { + madge(dir + '/core.js').obj().should.eql({ + 'core': [] + }); }); - it('should tackle errors in files', () => { - madge([__dirname + '/files/cjs/error.js']) - .obj().should.eql({'error': []}); + it('should exclude NPM modules by default', () => { + madge(dir + '/npm.js').obj().should.eql({ + 'normal/d': [], + 'npm': ['normal/d'] + }); }); - it('should be able to exclude modules', () => { - madge([__dirname + '/files/cjs/normal'], { - exclude: '^sub' - }).obj().should.eql({'a': [], 'd': [], 'fancy-main/not-index': []}); - - madge([__dirname + '/files/cjs/normal'], { - exclude: '.*\/c$' - }).obj().should.eql({'a': ['sub/b'], 'd': [], 'sub/b': [], 'fancy-main/not-index': []}); + it('should be able to include NPM modules', () => { + madge(dir + '/npm.js', { + includeNpm: true + }).obj().should.eql({ + 'node_modules/a': [], + 'normal/d': [], + 'npm': ['node_modules/a', 'normal/d'] + }); }); - it('should find circular dependencies', () => { - madge([__dirname + '/files/cjs/circular']) - .circular().getArray().should.eql([['a', 'b', 'c']]); + it('should be able to show file extensions', () => { + madge(dir + '/normal/a.js', { + showFileExtension: true + }).obj().should.eql({ + 'a.js': ['sub/b.js'], + 'd.js': [], + 'sub/b.js': ['sub/c.js'], + 'sub/c.js': ['d.js'] + }); }); - it('should compile coffeescript on-the-fly', () => { - madge([__dirname + '/files/cjs/coffeescript']) - .obj().should.eql({'a': ['./b'], 'b': []}); - }); }); diff --git a/test/es6.js b/test/es6.js index ca22c89a..314224ad 100644 --- a/test/es6.js +++ b/test/es6.js @@ -4,68 +4,68 @@ const madge = require('../lib/madge'); require('should'); -describe('module format (ES6)', () => { +describe('ES6', () => { + const dir = __dirname + '/files/es6'; - it('should behave as expected on ok files', () => { - madge([__dirname + '/files/es6/normal'], { - format: 'es6' - }).obj().should.eql({'a': ['sub/b'], 'fancy-main/not-index': [], 'd': [], 'sub/b': ['sub/c'], 'sub/c': ['d']}); + it('should find circular dependencies', () => { + madge(dir + '/circular/a.js').circular().should.eql([ + ['a', 'b', 'c'] + ]); }); it('should tackle errors in files', () => { - madge([__dirname + '/files/es6/error.js'], { - format: 'es6' - }).obj().should.eql({'error': []}); + madge(dir + '/error.js').obj().should.eql({ + 'error': [] + }); }); - it('should be able to exclude modules', () => { - madge([__dirname + '/files/es6/normal'], { - exclude: '^sub', - format: 'es6' - }).obj().should.eql({'a': [], 'd': [], 'fancy-main/not-index': []}); - - madge([__dirname + '/files/es6/normal'], { - exclude: '.*\/c$', - format: 'es6' - }).obj().should.eql({'a': ['sub/b'], 'd': [], 'sub/b': [], 'fancy-main/not-index': []}); - }); - - it('should find circular dependencies', () => { - madge([__dirname + '/files/es6/circular'], { - format: 'es6' - }).circular().getArray().should.eql([['a', 'b', 'c']]); + it('should find absolute imports from the root', () => { + madge(dir + '/absolute.js').obj().should.eql({ + 'absolute': ['absolute/a'], + 'absolute/a': [] + }); }); - it('should find absolute imports from the root', () => { - madge([__dirname + '/files/es6/absolute.js', __dirname + '/files/es6/absolute'], { - format: 'es6' - }).obj().should.eql({'absolute': ['absolute/a'], 'absolute/a': ['absolute/b'], 'absolute/b': []}); + it('should find imports on files with ES7', () => { + madge(dir + '/async.js').obj().should.eql({ + 'absolute/b': [], + 'async': ['absolute/b'] + }); }); - it('should find imports on files with jsx', () => { - madge([__dirname + '/files/es6/jsx.js'], { - format: 'es6' - }).obj().should.eql({'jsx': ['absolute/b']}); + it('should support export x from "./file"', () => { + madge(dir + '/re-export/c.js').obj().should.eql({ + 'a': [], + 'b-default': ['a'], + 'b-named': ['a'], + 'b-star': ['a'], + 'c': [ + 'b-default', + 'b-named', + 'b-star' + ] + }); }); - it('should find imports on files with ES7', () => { - madge([__dirname + '/files/es6/async.js'], { - format: 'es6' - }).obj().should.eql({'async': ['absolute/b']}); + it('should find imports on files with JSX content', () => { + madge(dir + '/jsx.js').obj().should.eql({ + 'jsx': ['absolute/b'], + 'absolute/b': [] + }); }); - it('should support export x from "./file"', () => { - madge([__dirname + '/files/es6/re-export'], { - format: 'es6' - }).obj().should.eql({'a': [], 'b-default': ['a'], 'b-named': ['a'], 'b-star': ['a'], 'c': ['b-default', 'b-named', 'b-star']}); + it('should find import in JSX files', () => { + madge(dir + '/jsx/basic.jsx').obj().should.eql({ + 'basic': ['other'], + 'other': [] + }); }); - it('can detect imports in JSX files', () => { - madge([__dirname + '/files/es6/jsx/basic.jsx'], { - format: 'es6' - }).obj().should.eql({basic: [ - '../../../../other', - '../../../../react' - ]}); + it('should be able to exclude modules', () => { + madge(dir + '/normal/a.js', { + exclude: '.*\/sub' + }).obj().should.eql({ + 'a': [] + }); }); }); diff --git a/test/files/amd/amdes6.js b/test/files/amd/amdes6.js index d981c2c1..40c07164 100644 --- a/test/files/amd/amdes6.js +++ b/test/files/amd/amdes6.js @@ -1,5 +1,5 @@ define([ - 'ok/a' + 'ok/d' ], function(a) { 'use strict'; diff --git a/test/files/amd/circular/main.js b/test/files/amd/circular/main.js new file mode 100644 index 00000000..928b1077 --- /dev/null +++ b/test/files/amd/circular/main.js @@ -0,0 +1,3 @@ +define(['a', 'f'], function () { + return 'MAIN'; +}); \ No newline at end of file diff --git a/test/files/amd/circularRelative/a.coffee b/test/files/amd/circularRelative/a.coffee deleted file mode 100644 index 70388131..00000000 --- a/test/files/amd/circularRelative/a.coffee +++ /dev/null @@ -1 +0,0 @@ -define 'a', ['./foo/b'], -> \ No newline at end of file diff --git a/test/files/amd/circularRelative/a.js b/test/files/amd/circularRelative/a.js new file mode 100644 index 00000000..2af72e2b --- /dev/null +++ b/test/files/amd/circularRelative/a.js @@ -0,0 +1,3 @@ +define('a', ['./foo/b'], function () { + return 'A'; +}); \ No newline at end of file diff --git a/test/files/amd/circularRelative/foo/b.coffee b/test/files/amd/circularRelative/foo/b.coffee deleted file mode 100644 index a6e3778e..00000000 --- a/test/files/amd/circularRelative/foo/b.coffee +++ /dev/null @@ -1 +0,0 @@ -define 'foo/b', ['a'], -> \ No newline at end of file diff --git a/test/files/amd/circularRelative/foo/b.js b/test/files/amd/circularRelative/foo/b.js new file mode 100644 index 00000000..1ca79c69 --- /dev/null +++ b/test/files/amd/circularRelative/foo/b.js @@ -0,0 +1,3 @@ +define('foo/b', ['../a'], function () { + return 'B'; +}); \ No newline at end of file diff --git a/test/files/amd/plugin.js b/test/files/amd/plugin.js index 8d557c96..86e6fdf7 100644 --- a/test/files/amd/plugin.js +++ b/test/files/amd/plugin.js @@ -1,3 +1,3 @@ -define(['ok/a', './locale!locales/not/exists'], function (D) { +define(['ok/d', './locale!locales/not/exists'], function (D) { return 'p'; }); \ No newline at end of file diff --git a/test/files/amd/relative/a.js b/test/files/amd/relative/a.js deleted file mode 100644 index 471690b0..00000000 --- a/test/files/amd/relative/a.js +++ /dev/null @@ -1,3 +0,0 @@ -define(function () { - return 'A'; -}); \ No newline at end of file diff --git a/test/files/amd/relative/b.js b/test/files/amd/relative/b.js deleted file mode 100644 index dd71ff83..00000000 --- a/test/files/amd/relative/b.js +++ /dev/null @@ -1,3 +0,0 @@ -define(['./a'], function (A) { - return 'B'; -}); \ No newline at end of file diff --git a/test/files/amd/relative/foo/bar/d.js b/test/files/amd/relative/foo/bar/d.js deleted file mode 100644 index ea8cd93c..00000000 --- a/test/files/amd/relative/foo/bar/d.js +++ /dev/null @@ -1,3 +0,0 @@ -define(['../../a'], function (A) { - return 'D'; -}); \ No newline at end of file diff --git a/test/files/amd/relative/foo/c.js b/test/files/amd/relative/foo/c.js deleted file mode 100644 index 9c651c76..00000000 --- a/test/files/amd/relative/foo/c.js +++ /dev/null @@ -1,3 +0,0 @@ -define(['../a'], function (A) { - return 'C'; -}); \ No newline at end of file diff --git a/test/files/cjs/core.js b/test/files/cjs/core.js new file mode 100644 index 00000000..48f6d7b9 --- /dev/null +++ b/test/files/cjs/core.js @@ -0,0 +1,2 @@ +var fs = require('fs'); +var a = require('a'); \ No newline at end of file diff --git a/test/files/cjs/npm.js b/test/files/cjs/npm.js new file mode 100644 index 00000000..4a6d2286 --- /dev/null +++ b/test/files/cjs/npm.js @@ -0,0 +1,2 @@ +var a = require('a'); +var d = require('./normal/d'); \ No newline at end of file diff --git a/test/files/es6/absolute.js b/test/files/es6/absolute.js index 317bafc6..f1a71933 100644 --- a/test/files/es6/absolute.js +++ b/test/files/es6/absolute.js @@ -1 +1 @@ -import {A} from 'test/files/es6/absolute/a'; \ No newline at end of file +import {A} from 'absolute/a'; \ No newline at end of file diff --git a/test/files/es6/async.js b/test/files/es6/async.js index 4b22eaeb..9f5637e0 100644 --- a/test/files/es6/async.js +++ b/test/files/es6/async.js @@ -1,2 +1,2 @@ -import {B} from 'test/files/es6/absolute/b'; +import {B} from 'absolute/b'; async function foo() {} diff --git a/test/files/es6/jsx.js b/test/files/es6/jsx.js index d5235b45..b2662a0e 100644 --- a/test/files/es6/jsx.js +++ b/test/files/es6/jsx.js @@ -1,2 +1,2 @@ -import {B} from 'test/files/es6/absolute/b'; +import {B} from 'absolute/b'; var templ = ; diff --git a/test/madge.js b/test/madge.js index 430b1f33..26fb8323 100644 --- a/test/madge.js +++ b/test/madge.js @@ -5,56 +5,15 @@ const madge = require('../lib/madge'); require('should'); describe('Madge', () => { - describe('source argument', () => { - it('should handle a string as argument', () => { - madge('test/files/cjs/normal/a.js').obj().should.eql({a: ['sub/b']}); - }); - - it('should handle an array as argument', () => { - madge(['test/files/cjs/normal/a.js']).obj().should.eql({a: ['sub/b']}); - }); - - it('should handle a object as argument', () => { - madge({ - a: ['b', 'c'], - b: ['c'], - c: [] - }).obj().should.eql({a: ['b', 'c'], b: ['c'], c: []}); - }); - }); - - describe('paths', () => { - it('should be ok with relative paths', () => { - madge(['test/files/cjs/normal/a.js']).obj().should.eql({a: ['sub/b']}); - }); - }); - - describe('basedir', () => { - it('should use common dir when given multiple paths (cjs)', () => { - madge([__dirname + '/files/cjs/multibase/1', __dirname + '/files/cjs/multibase/2']).obj().should.eql({'1/a': [], '2/b': []}); - }); - - it('should use common dir when given multiple paths (amd)', () => { - madge([__dirname + '/files/amd/multibase/foo', __dirname + '/files/amd/multibase/bar'], { - format: 'amd' - }).obj().should.eql({'foo/a': [], 'bar/b': []}); - }); - }); - - describe('extensions', () => { - it('should be ok with custom extensions', () => { - madge(['test/files/cjs/extensions'], {extensions: ['.js', '.cjs']}).obj().should.eql({a: ['b'], b: []}); + describe('#constructor', () => { + it('should throw error on missing filename argument', () => { + (() => { + madge(); + }).should.throw('Filename argument is missing'); }); }); - - describe('.tree', () => { - it('should accessible as an object', () => { - madge({a: ['b', 'c']}).tree.should.be.an.Object; - }); - }); - describe('#obj', () => { - it('should return tree as object with dependencies as arrays', () => { + it('should return dependency object', () => { madge({a: ['b', 'c']}).obj().should.eql({a: ['b', 'c']}); }); }); @@ -68,4 +27,14 @@ describe('Madge', () => { }).dot().should.eql('digraph G {\n "a";\n "b";\n "c";\n "a" -> "b";\n "a" -> "c";\n "b" -> "c";\n}\n'); }); }); + + describe('#depends', () => { + it('should return modules that depends on another', () => { + madge({ + a: ['b', 'c'], + b: ['c'], + c: [] + }).depends('c').should.eql(['a', 'b']); + }); + }); }); diff --git a/test/pluggable.js b/test/pluggable.js deleted file mode 100644 index e2e794fd..00000000 --- a/test/pluggable.js +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-env mocha */ -'use strict'; - -const madge = require('../lib/madge'); -require('should'); - -describe('Madge', () => { - describe('pluggable', () => { - it('should serve parseFile and addModule events for cjs', () => { - let fileAdd = ''; - let idAdd = ''; - const opts = {}; - opts.onParseFile = (obj) => { - const arr = obj.filename.split('/'); - fileAdd += arr[arr.length - 1]; - }; - opts.onAddModule = (obj) => { - idAdd += obj.id; - }; - madge([__dirname + '/files/cjs/normal'], opts); - (fileAdd + idAdd).should.eql('a.jsd.jsnot-index.jsb.jsc.js' + 'adfancy-main/not-indexsub/bsub/c'); - }); - }); - - describe('pluggable - amd', () => { - it('should serve parseFile and addModule events for amd', () => { - let fileAdd = ''; - let idAdd = ''; - const opts = {}; - opts.onParseFile = (obj) => { - const arr = obj.filename.split('/'); - fileAdd += arr[arr.length - 1]; - }; - opts.onAddModule = (obj) => { - idAdd += obj.id; - }; - opts.format = 'amd'; - madge([__dirname + '/files/amd/ok'], opts); - (idAdd + fileAdd).should.eql('adesub/bsub/c' + 'a.jsd.jse.jsb.jsc.js'); - }); - }); - - describe('pluggable - scope', () => { - it('should add idAdd property to the returned madger', () => { - const opts = {}; - opts.onAddModule = function (obj) { - if (this.idAdd) { - this.idAdd += obj.id; - } else { - this.idAdd = obj.id; - } - }; - const madger = madge([__dirname + '/files/cjs/normal'], opts); - madger.idAdd.should.eql('adfancy-main/not-indexsub/bsub/c'); - }); - }); -}); From c7c740d2c3c07419b8cf879d60bb902c0988866a Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Thu, 21 Jul 2016 09:18:05 +0200 Subject: [PATCH 02/39] Refactor API to use dependency-tree --- lib/cyclic.js | 27 +--------- lib/graph.js | 81 +++++++++++------------------ lib/madge.js | 141 ++++++++++++++++++++++++++++++++++++++------------ lib/print.js | 11 ++-- 4 files changed, 143 insertions(+), 117 deletions(-) diff --git a/lib/cyclic.js b/lib/cyclic.js index a4acfcdf..5fcc376d 100644 --- a/lib/cyclic.js +++ b/lib/cyclic.js @@ -48,7 +48,7 @@ function resolver(id, modules, circular, resolved, unresolved) { /** * Finds all circular dependencies for the given modules. * @param {Object} modules - * @return {Object} + * @return {Array} */ module.exports = function (modules) { const circular = []; @@ -59,28 +59,5 @@ module.exports = function (modules) { resolver(id, modules, circular, resolved, unresolved); }); - return { - /** - * Expose the circular dependency array. - * @return {Array} - */ - getArray() { - return circular; - }, - - /** - * Check if the given module is part of a circular dependency. - * @param {String} id - * @return {Boolean} - */ - isCyclic(id) { - let cyclic = false; - circular.forEach((path) => { - if (path.indexOf(id) >= 0) { - cyclic = true; - } - }); - return cyclic; - } - }; + return circular; }; diff --git a/lib/graph.js b/lib/graph.js index 26a295a4..58d76847 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -9,20 +9,11 @@ const graphviz = require('graphviz'); * @param {Object} node * @param {String} color */ -function nodeColor(node, color) { +function setNodeColor(node, color) { node.set('color', color); node.set('fontcolor', color); } -/** - * Set color for nodes without dependencies. - * @param {Object} node - * @param {String} [color] - */ -function noDependencyNode(node, color) { - nodeColor(node, color || '#cfffac'); -} - /** * Check if Graphviz is installed on the system. * @throws Error @@ -37,76 +28,64 @@ function checkGraphvizInstalled() { /** * Return options to use with graphviz digraph. - * @param {Object} opts + * @param {Object} config * @return {Object} */ -function createGraphvizOptions(opts) { - // Valid attributes: http://www.graphviz.org/doc/info/attrs.html - const G = { - layout: opts.layout || 'dot', - overlap: false, - bgcolor: '#ffffff' - }; - - const N = { - fontname: opts.fontFace || 'Times-Roman', - fontsize: opts.fontSize || 14 - }; - - const 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'; - } - +function createGraphvizOptions(config) { return { 'type': 'png', - 'G': G, - 'E': E, - 'N': N + 'G': { + overlap: false, + layout: config.layout, + bgcolor: config.backgroundColor + }, + 'E': { + color: config.edgeColor + }, + 'N': { + fontname: config.fontName, + fontsize: config.fontSize, + color: config.nodeColor, + fontcolor: config.nodeColor + } }; } /** * Creates a PNG image from the module dependency graph. * @param {Object} modules - * @param {Object} opts + * @param {Object} config * @param {Function} callback */ -module.exports.image = function (modules, opts, callback) { +module.exports.image = function (modules, config, callback) { const g = graphviz.digraph('G'); const nodes = {}; checkGraphvizInstalled(); - opts.imageColors = opts.imageColors || {}; - - const cyclicResults = cyclic(modules); + const cyclicModules = cyclic(modules).reduce((a, b) => a.concat(b), []); Object.keys(modules).forEach((id) => { - nodes[id] = nodes[id] || g.addNode(id); - if (opts.colors && modules[id]) { - if (!modules[id].length) { - noDependencyNode(nodes[id], opts.imageColors.noDependencies); - } else if (cyclicResults.isCyclic(id)) { - nodeColor(nodes[id], (opts.imageColors.circular || '#ff6c60')); - } + + if (!modules[id].length) { + setNodeColor(nodes[id], config.noDependenciesColor); + } else if (cyclicModules.indexOf(id) >= 0) { + setNodeColor(nodes[id], config.circularDependencyColor); } modules[id].forEach((depId) => { nodes[depId] = nodes[depId] || g.addNode(depId); - if (opts.colors && !modules[depId]) { - noDependencyNode(nodes[depId], opts.imageColors.noDependencies); + + if (!modules[depId]) { + setNodeColor(nodes[depId], config.noDependenciesColor); } + g.addEdge(nodes[id], nodes[depId]); }); }); - g.output(createGraphvizOptions(opts), callback); + g.output(createGraphvizOptions(config), callback); }; /** diff --git a/lib/madge.js b/lib/madge.js index 7c4d1e1d..b3ef3aff 100644 --- a/lib/madge.js +++ b/lib/madge.js @@ -1,56 +1,130 @@ 'use strict'; +const fs = require('fs'); +const path = require('path'); +const dependencyTree = require('dependency-tree'); const cyclic = require('./cyclic'); -const CJS = require('./parse/cjs'); -const AMD = require('./parse/amd'); -const ES6 = require('./parse/es6'); const graph = require('./graph'); +const debug = require('debug')('madge'); + +const defaultConfig = { + showFileExtension: false, + includeNpm: false, + requireConfig: null, + webpackConfig: null, + layout: 'dot', + fontName: 'Arial', + fontSize: '14px', + backgroundColor: '#000000', + nodeColor: '#c6c5fe', + noDependenciesColor: '#cfffac', + circularDependencyColor: '#ff6c60', + edgeColor: '#757575' +}; class Madge { /** * Class constructor. * @constructor * @api public - * @param {String|Array|Object} src - * @param {Object} opts + * @param {String|Object} filename + * @param {Object} config */ - constructor(src, opts) { - let tree = []; + constructor(filename, config) { + if (!filename) { + throw new Error('Filename argument is missing'); + } - this.opts = opts || {}; - this.opts.format = String(this.opts.format || 'cjs').toLowerCase(); + this.config = Object.assign({}, defaultConfig, config); + debug('using config', this.config); - if (typeof src === 'object' && !Array.isArray(src)) { - this.tree = src; - return; - } + this.excludeRegex = this.config.exclude ? new RegExp(this.config.exclude) : false; + this.rootDirectory = this.config.directory ? path.resolve(this.config.directory) : path.dirname(filename); - if (typeof src === 'string') { - src = [src]; - } + if (typeof filename === 'object') { + this.tree = filename; + } else { + if (!fs.statSync(filename).isFile()) { // eslint-disable-line no-sync + throw new Error('Directory not supported'); + } - if (src && src.length) { - tree = this.parse(src); + this.tree = this.convertDependencyTree(dependencyTree({ + filename: filename, + directory: this.rootDirectory, + requireConfig: this.config.requireConfig, + webpackConfig: this.config.webpackConfig, + filter: this.pathFilter.bind(this) + })); } - this.tree = tree; + this.sortDependencies(); } /** - * Parse the given source folder(s). - * @param {Array|Object} src + * Function used to determine if a module (and its subtree) should be included in the dependency tree. + * @param {String} path + * @return {Boolean} + */ + pathFilter(path) { + return (this.config.includeNpm || path.indexOf('node_modules') < 0) && !this.isExcluded(path); + } + + /** + * Convert deep tree produced by `dependency-tree` to internal format used by Madge. + * @param {Object} tree + * @param {Object} [graph] * @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 + '"'); + convertDependencyTree(tree, graph) { + graph = graph || {}; + + Object.keys(tree).forEach((key) => { + const id = this.processPath(key); + + if (!graph[id]) { + graph[id] = Object + .keys(tree[key]) + .map((dep) => this.processPath(dep)); + } + + this.convertDependencyTree(tree[key], graph); + }); + + return graph; + } + + /** + * Process path. + * @param {String} absPath + * @return {String} + */ + processPath(absPath) { + absPath = path.relative(this.rootDirectory, absPath); + + if (!this.config.showFileExtension) { + absPath = absPath.replace(/\.\w+$/, ''); } + + return absPath; + } + + /** + * Sort dependencies by name. + */ + sortDependencies() { + this.tree = Object.keys(this.tree).sort().reduce((acc, id) => { + (acc[id] = this.tree[id]).sort(); + return acc; + }, {}); + } + + /** + * Check if module should be excluded. + * @param {String} id + * @return {Boolean} + */ + isExcluded(id) { + return this.excludeRegex && id.match(this.excludeRegex); } /** @@ -102,12 +176,11 @@ class Madge { /** * 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); + image(callback) { + graph.image(this.tree, this.config, callback); } } -module.exports = (src, opts) => new Madge(src, opts); +module.exports = (filename, config) => new Madge(filename, config); diff --git a/lib/print.js b/lib/print.js index 1b7d3f69..e82eeb50 100644 --- a/lib/print.js +++ b/lib/print.js @@ -71,21 +71,19 @@ module.exports.summary = function (modules, opts) { /** * Print the result from Madge.circular(). - * @param {Object} circular + * @param {Array} circular * @param {Object} opts * @return {undefined} */ module.exports.circular = function (circular, opts) { - const arr = circular.getArray(); - if (opts.output === 'json') { - return process.stdout.write(toJSON(arr)); + return process.stdout.write(toJSON(circular)); } - if (!arr.length) { + if (!circular.length) { console.log(c('No circular dependencies found!', 'green', opts.colors)); } else { - arr.forEach((path, idx) => { + circular.forEach((path, idx) => { path.forEach((module, idx) => { if (idx) { process.stdout.write(c(' -> ', 'cyan', opts.colors)); @@ -94,7 +92,6 @@ module.exports.circular = function (circular, opts) { }); process.stdout.write('\n'); }); - process.exit(1); } }; From 3f7bb34a02cd5f220c470ef4c0886d904c32d701 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Thu, 21 Jul 2016 09:18:26 +0200 Subject: [PATCH 03/39] Refactor CLI to use new API --- bin/cli.js | 110 ++++++++++++++++++++++++++++++++++++++++ bin/madge | 146 ----------------------------------------------------- 2 files changed, 110 insertions(+), 146 deletions(-) create mode 100755 bin/cli.js delete mode 100755 bin/madge diff --git a/bin/cli.js b/bin/cli.js new file mode 100755 index 00000000..48a74ec8 --- /dev/null +++ b/bin/cli.js @@ -0,0 +1,110 @@ +#!/usr/bin/env node +'use strict'; + +const fs = require('fs'); +const version = require('../package.json').version; +const program = require('commander'); +const printResult = require('../lib/print'); +const madge = require('../lib/madge'); +const rc = require('rc')('madge'); +const debug = require('debug')('madge'); + +program + .version(version) + .usage('[options] ') + .option('--directory ', '') + .option('--list', 'show list of all dependencies (default)') + .option('--summary', 'show summary of all dependencies') + .option('--json', 'show list of dependencies as JSON') + .option('--circular', 'show circular dependencies') + .option('--depends', 'show modules that depends on the given id') + .option('--image ', 'write graph to file as a PNG image') + .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') + .option('--dot', 'show graph using the DOT language') + .option('--no-color', 'disable color in output and image', false) + .option('--require-config ', 'path to RequireJS config') + .option('--webpack-config ', 'path to webpack config') + .parse(process.argv); + +if (!program.args.length) { + console.log(program.helpInformation()); + process.exit(1); +} + +if (rc.config) { + debug('using runtime configuration from %s', rc.config); +} + +const config = Object.assign({}, rc); + +delete config._; +delete config.config; +delete config.configs; + +['directory', 'layout', 'requireConfig', 'webpackConfig'].forEach((option) => { + if (program[option]) { + config[option] = program[option]; + } +}); + +if (!program.color) { + config.backgroundColor = '#ffffff'; + config.nodeColor = '#00000'; + config.noDependenciesColor = '#00000'; + config.circularDependencyColor = '#000000'; + config.edgeColor = '#757575'; +} + +const res = madge(program.args[0], config); + +if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot && !program.json)) { + printResult.list(res.obj(), { + colors: program.color, + output: program.output + }); +} + +if (program.summary) { + printResult.summary(res.obj(), { + colors: program.color, + output: program.output + }); +} + +if (program.json) { + process.stdout.write(JSON.stringify(res.tree) + '\n'); +} + +if (program.circular) { + const circular = res.circular(); + + printResult.circular(circular, { + colors: program.color, + output: program.output + }); + + if (circular.length) { + process.exit(1); + } +} + +if (program.depends) { + printResult.depends(res.depends(program.depends), { + colors: program.color, + output: program.output + }); +} + +if (program.image) { + res.image((image) => { + fs.writeFile(program.image, image, (err) => { + if (err) { + throw err; + } + }); + }); +} + +if (program.dot) { + process.stdout.write(res.dot()); +} diff --git a/bin/madge b/bin/madge deleted file mode 100755 index 8f482bb0..00000000 --- a/bin/madge +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env node - -'use strict'; - -/** - * Module dependencies - */ -const fs = require('fs'); -const version = require('../package.json').version; -const program = require('commander'); -const printResult = require('../lib/print'); -const madge = require('../lib/madge'); - -program - .version(version) - .usage('[options] ') - .option('-f, --format ', 'format to parse (amd/cjs/es6)', 'cjs') - .option('-L, --list', 'show list of all dependencies (default)') - .option('-s, --summary', 'show summary of all dependencies') - .option('-c, --circular', 'show circular dependencies') - .option('-d, --depends ', 'show modules that depends on the given id') - .option('-x, --exclude ', 'a regular expression for excluding modules') - .option('-t, --dot', 'output graph in the DOT language') - .option('-i, --image ', 'write graph to file as a PNG image') - .option('-l, --layout ', 'layout engine to use for image graph (dot/neato/fdp/sfdp/twopi/circo)', 'dot') - .option('-b, --break-on-error', 'break on parse errors & missing modules', false) - .option('-n, --no-colors', 'skip colors in output and images', false) - .option('-r, --read', 'skip scanning folders and read JSON from stdin') - .option('-C, --config ', 'provide a config file') - .option('-R, --require-config ', 'include shim dependencies and paths found in RequireJS config file') - .option('-O, --optimized', 'if given file is optimized with r.js', false) - .option('-N, --find-nested-dependencies', 'find nested dependencies in AMD modules', false) - .option('-M, --main-require-module ', 'name of the primary RequireJS module, if it\'s included with `require()`', '') - .option('-j --json', 'output dependency tree in json') - .option('-p --paths ', 'additional comma separated paths to search for dependencies (CJS only)', '') - .option('-e --extensions ', 'comma separated string of valid file extensions', 'js,coffee') - .parse(process.argv); - -if (!program.args.length && !program.read && !program.requireConfig) { - console.log(program.helpInformation()); - process.exit(1); -} - -let src = program.args; - -// Check config file -if (program.config && fs.existsSync(program.config)) { // eslint-disable-line no-sync - const configOptions = JSON.parse(fs.readFileSync(program.config, 'utf8')); // eslint-disable-line no-sync - // Duck punch the program with the new options - // Config file take precedence - for (const k in configOptions) { - if (configOptions.hasOwnProperty(k)) { - program[k] = configOptions[k]; - } - } -} - -// Read from standard input -if (program.read) { - let buffer = ''; - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - process.stdin.on('data', (chunk) => { - buffer += chunk; - }); - process.stdin.on('end', () => { - src = JSON.parse(buffer); - run(); - }); -} else { - run(); -} - -function run() { - // Start parsing - const res = madge(src, { - format: program.format, - breakOnError: program.breakOnError, - exclude: program.exclude, - optimized: program.optimized, - requireConfig: program.requireConfig, - mainRequireModule: program.mainRequireModule, - paths: program.paths ? program.paths.split(',') : undefined, - extensions: program.extensions.split(',').map((str) => '.' + str), - findNestedDependencies: program.findNestedDependencies - }); - - // Ouput summary - if (program.summary) { - printResult.summary(res.obj(), { - colors: program.colors, - output: program.output - }); - } - - // Output circular dependencies - if (program.circular) { - printResult.circular(res.circular(), { - colors: program.colors, - output: program.output - }); - } - - // Output module dependencies - if (program.depends) { - printResult.depends(res.depends(program.depends), { - colors: program.colors, - output: program.output - }); - } - - // Write image - if (program.image) { - res.image({ - colors: program.colors, - layout: program.layout, - fontFace: program.font, - fontSize: program.fontSize, - imageColors: program.imageColors - }, (image) => { - fs.writeFile(program.image, image, (err) => { - if (err) { - throw err; - } - }); - }); - } - - // Output DOT - if (program.dot) { - process.stdout.write(res.dot()); - } - - // Output JSON - if (program.json) { - process.stdout.write(JSON.stringify(res.tree) + '\n'); - } - - // Output text (default) - if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot && !program.json)) { - printResult.list(res.obj(), { - colors: program.colors, - output: program.output - }); - } -} From 2869b44b660ddf687b6e09b30318173ce49a5870 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Thu, 21 Jul 2016 09:18:49 +0200 Subject: [PATCH 04/39] Update README --- .gitignore | 6 +- LICENSE | 9 -- README.md | 251 +++++++++++++++--------------------------- examples/express.png | Bin 177160 -> 0 bytes examples/madge.png | Bin 0 -> 33793 bytes examples/terminal.png | Bin 84514 -> 0 bytes 6 files changed, 88 insertions(+), 178 deletions(-) delete mode 100644 LICENSE delete mode 100644 examples/express.png create mode 100644 examples/madge.png delete mode 100644 examples/terminal.png diff --git a/.gitignore b/.gitignore index c2f87f8d..5ed6a505 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,2 @@ -gen.sh -config.json node_modules -!test/files/cjs/node_modules -/.project -/.settings +.madgerc \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 98011770..00000000 --- a/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2012 Patrik Henningsson - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index a64fec2d..789a5c34 100644 --- a/README.md +++ b/README.md @@ -7,33 +7,32 @@ [![NPM Status](http://img.shields.io/npm/dm/madge.svg?style=flat-square)](https://www.npmjs.org/package/madge) [![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](https://paypal.me/pahen) -Create graphs from your [CommonJS](http://nodejs.org/api/modules.html), [AMD](https://github.com/amdjs/amdjs-api/wiki/AMD) or [ES6](https://people.mozilla.org/~jorendorff/es6-draft.html) module dependencies. Could also be useful for finding circular dependencies in your code. Tested on [Node.js](http://nodejs.org/) and [RequireJS](http://requirejs.org/) projects. Dependencies are calculated using static code analysis. CommonJS dependencies are found using James Halliday's [detective](https://github.com/substack/node-detective), for AMD I'm using [amdetective](https://www.npmjs.org/package/amdetective) and for ES6 [detective-es6](https://www.npmjs.com/package/detective-es6) is used. Modules written in [CoffeeScript](http://coffeescript.org/) with extension .coffee are supported and will automatically be compiled on-the-fly. +Can be used for creating graphs from your dependencies or find circular dependencies in your code. The dependencies are calculated using Joel Kemp's awesome [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree). + +Works for JS (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus); basically, any filetype supported by [precinct](https://github.com/mrjoelkemp/node-precinct). + + - For CommonJS modules, 3rd party dependencies (npm installed dependencies) are included in the tree by default + - Dependency path resolutions are handled by [filing-cabinet](https://github.com/mrjoelkemp/node-filing-cabinet) + - Supports RequireJS and Webpack loaders + - All core Node modules (assert, path, fs, etc) are removed from the dependency list by default + +See [release notes](#release-notes) for latest changes. ## Examples Here's a very simple example of a generated image. -![](https://github.com/pahen/node-madge/raw/master/examples/small.png) +![](examples/small.png) - blue = has dependencies - green = has no dependencies - red = has circular dependencies -Here's an example generated from the [Express](https://github.com/visionmedia/express) project. +Here's an example generated from the madge source using the command `madge bin/cli.js --directory . --image examples/madge.png`. -![](https://github.com/pahen/node-madge/raw/master/examples/express.png) - -And some terminal usage. - -![](https://github.com/pahen/node-madge/raw/master/examples/terminal.png) +![](examples/madge.png) # Installation -To install as a library: - - $ npm install madge - -To install the CLI: - $ npm -g install madge ## Graphviz (optional) @@ -42,11 +41,7 @@ Only required if you want to generate the visual graphs using [Graphviz](http:// ### Mac OS X - $ port install graphviz - -OR - - $ brew install graphviz + $ brew install graphviz || port install graphviz ### Ubuntu @@ -54,175 +49,128 @@ OR # API - var madge = require('madge'); - var dependencyObject = madge('./'); - console.log(dependencyObject.tree); - -## madge(src, opts) +## madge(filename: string, config: object) -{Object|Array|String} **src** (required) +> `config` is optional and should be [configuration](#configuration) to be used. -- Object - a dependency tree. -- Array - an Array of directories to scan. -- String - a directory to scan. +Returns a `Promise` resolved with the Madge instance object. -{Object} **opts** (optional) +## Functions -- {String} **format**. The module format to expect, 'cjs', 'amd' or 'es6'. Commonjs (cjs) is the default format. -- {String} **exclude**. String from which a regex will be constructed for excluding files from the scan. -- {Boolean} **breakOnError**. True if the parser should stop on parse errors and when modules are missing, false otherwise. Defaults to false. -- {Boolean} **optimized**. True if the parser should read modules from a optimized file (r.js). Defaults to false. -- {Boolean} **findNestedDependencies**. True if nested dependencies should be found in AMD modules. Defaults to false. -- {String} **mainRequireModule**. Name of the module if parsing an optimized file (r.js), where the main file used `require()` instead of `define`. Defaults to `''`. -- {String} **requireConfig**. Path to RequireJS config used to find shim dependencies and path aliases. Not used by default. -- {Function} **onParseFile**. Function to be called when parsing a file (argument will be an object with "filename" and "src" property set). -- {Function} **onAddModule** . Function to be called when adding a module to the module tree (argument will be an object with "id" and "dependencies" property set). -- {Array} **extensions**. List of file extensions which are considered. Defaults to `['.js']`. - -## dependency object (returned from madge) - -#### .opts +#### .obj() -Options object passed used in the constructor. +> Returns an `Object` with all dependencies. -#### .tree + const madge = require('madge'); -Dependency tree object. Can be overwritten with an object in the format: + madge('path/to/app.js').then((res) => { + console.log(res.obj()); + }); - { - 'module1': ['dep1a', 'dep1b'], - 'module2': ['dep2a'] - } +#### .circular() -#### .obj() +> Returns an `Array` with all modules that has circular dependencies. -Alias to the tree property. + const madge = require('madge'); -#### .circular() + madge('path/to/app.js').then((res) => { + console.log(res.circular()); + }); -Circular dependencies object, returns: +#### .depends() - { - 'getArray': function, /** @param {} array */ - 'isCyclic': function /** @param {id} boolean */ - } +> Returns an `Array` with all modules that depends on a given module. -#### .depends() + const madge = require('madge'); -Returns a list of modules that depends on a given module. + madge('path/to/app.js').then((res) => { + console.log(res.depends()); + }); #### .dot() -Get a DOT representation of the module dependency graph. +> Returns a `String` with a DOT representation of the module dependency graph. -#### .image(opts, callback) + const madge = require('madge'); -Get an image representation of the module dependency graph. + madge('path/to/app.js').then((res) => { + console.log(res.dot()); + }); -- {Object} **opts** (required). - - {String} **layout**. The layout to use. Defaults to 'DOT'. - - {String} **fontFace**. The font face to use. Defaults to 'Times-Roman'. - - {Object} **imageColors**. Object with color information (all colors are strings containing hex values). - - {String} **bgcolor**. The backgound color. - - {String} **edge**. The edge color. - - {String} **dependencies**. The color for dependencies and for text if fontColor is not present. - - {String} **fontColor**. The color for text. -- {Function} **callback** (required). Receives the rendered image as the first argument. +#### .image() -# CLI - - Usage: madge [options] +> Returns a `Promise` resolved with an image representation of the module dependency graph. - Options: + const madge = require('madge'); - -h, --help output usage information - -V, --version output the version number - -f, --format format to parse (amd/cjs/es6) - -s, --summary show summary of all dependencies - -L, --list show list of all dependencies - -c, --circular show circular dependencies - -d, --depends show modules that depends on the given id - -x, --exclude a regular expression for excluding modules - -t, --dot output graph in the DOT language - -i, --image write graph to file as a PNG image - -l, --layout layout engine to use for image graph (dot/neato/fdp/sfdp/twopi/circo) - -b, --break-on-error break on parse errors & missing modules - -n, --no-colors skip colors in output and images - -r, --read skip scanning folders and read JSON from stdin - -C, --config provide a config file - -R, --require-config include shim dependencies and path aliases found in RequireJS config file - -O, --optimized if given file is optimized with r.js - -M --main-require-module name of the primary RequireJS module, if it's included with `require()` - -j --json output dependency tree in json + madge('path/to/app.js') + .then((res) => res.image()) + .then((image) => { + // write image to file + }); + }); +# Configuration -## Examples: +Property | Type | Default | Description +--- | --- | --- | --- +`includeNpm` | Boolean | false | If node_modules should be included +`showFileExtension` | Boolean | false | If file extensions should be included in module name +`requireConfig` | String | null | RequireJS config for resolving aliased modules +`webpackConfig` | String | null | Webpack config for resolving aliased modules +`layout` | String | dot | Layout to use in graph +`fontName` | String | Arial | Font name to use in graph +`fontSize` | String | 14px | Font size to use in graph +`backgroundColor` | String | #000000 | Background color for the graph +`nodeColor` | String | #c6c5fe | The default node color to use in the graph +`noDependenciesColor` | String | #cfffac | Color to use for nodes with dependencies +`circularDependencyColor` | String | #ff6c60 | The color to used for circular dependencies +`edgeColor` | String | #757575 | The edge color to use in the graph -### List all module dependencies (CommonJS) +> Note that when running the CLI it's possible to use a runtime configuration file. The config should placed in `.madgerc` in your project or home folder. Look [here](https://github.com/dominictarr/rc#standards) for alternative locations for the file. Here's an example: - $ madge /path/src + { + "showFileExtension": true, + "fontSize": "10px" + } -### List all module dependencies (AMD) +# CLI - $ madge --format amd /path/src +## Examples -### List all module dependencies (ES6) +### List all module dependencies - $ madge --format es6 /path/src + $ madge path/src/app.js ### Finding circular dependencies - $ madge --circular /path/src + $ madge --circular path/src/app.js ### Show modules that depends on a given module - $ madge --depends 'wheels' /path/src + $ madge --depends 'wheels' path/src/app.js ### Excluding modules - $ madge --exclude '^foo$|^bar$|^tests' /path/src + $ madge --exclude '^foo$|^bar$|^tests' path/src/app.js ### Save graph as a PNG image (graphviz required) - $ madge --image graph.png /path/src + $ madge --image graph.png path/src/app.js ### Save graph as a [DOT](http://en.wikipedia.org/wiki/DOT_language) file for further processing (graphviz required) - $ madge --dot /path/src > graph.gv + $ madge --dot path/src/app.js > graph.gv -### Run on optimized file by r.js (RequireJS optimizer) - $ r.js -o app-build.js - $ madge --format amd --optimized app-build.js +# Debugging -### Include shim dependencies found in RequireJS config - $ madge --format amd --require-config path/config.js path/src +To enable debugging output if you encounter problems, run madge in the following way -### Pipe predefined results (the example image was produced with the following command) + $ DEBUG=* madge path/src/app.js - $ cat << EOF | madge --read --image example.png - { - "a": ["b", "c", "d"], - "b": ["c"], - "c": [], - "d": ["a"] - } - EOF - -## Config (use with --config) +# Running tests - { - "format": "amd", - "image": "dependencyMap.png", - "fontFace": "Arial", - "fontSize": "14px", - "imageColors": { - "noDependencies" : "#0000ff", - "dependencies" : "#00ff00", - "circular" : "#bada55", - "edge" : "#666666", - "bgcolor": "#ffffff" - } - } + $ npm test # FAQ @@ -247,10 +195,6 @@ minimize a global energy function, which is equivalent to statistical multi-dime * **circo** circular layout, after Six and Tollis 99, Kauffman and Wiese 02. This is suitable for certain diagrams of multiple cyclic structures, such as certain telecommunications networks. -# Running tests - - $ npm test - # Release Notes ## v0.6.0 (July 06, 2016) @@ -324,10 +268,10 @@ minimize a global energy function, which is equivalent to statistical multi-dime * AMD plugins are now ignored as dependencies. Fixes [issue](https://github.com/pahen/grunt-madge/issues/1). ## v0.1.5 (September 04, 2013) -* Fixed Windows [issue](https://github.com/pahen/node-madge/issues/17) when reading from standard input with --read. +* Fixed Windows [issue](https://github.com/pahen/madge/issues/17) when reading from standard input with --read. ## v0.1.4 (January 10, 2013) -* Switched library for walking directory tree which should solve issues on [Windows](https://github.com/pahen/node-madge/issues/8). +* Switched library for walking directory tree which should solve issues on [Windows](https://github.com/pahen/madge/issues/8). ## v0.1.3 (December 28, 2012) * Added proper exit code when running "madge --circular" so it can be used in build scripts. @@ -358,25 +302,4 @@ minimize a global energy function, which is equivalent to statistical multi-dime # License -(The MIT License) - -Copyright (c) 2012 Patrik Henningsson <patrik.henningsson@gmail.com> - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +MIT License \ No newline at end of file diff --git a/examples/express.png b/examples/express.png deleted file mode 100644 index dd47c522c1211197a60ff53ddf68bd4a4006f24a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177160 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%0DaR|?sD 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�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 HXRrPrK83ji diff --git a/examples/madge.png b/examples/madge.png new file mode 100644 index 0000000000000000000000000000000000000000..86f644304de5b4ed9aea75d9232bf5f6b1c7a41d GIT binary patch literal 33793 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 literal 0 HcmV?d00001 diff --git a/examples/terminal.png b/examples/terminal.png deleted file mode 100644 index 87956eec295f49aeb3c875c6533302b67181d3d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84514 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%0DqH-+KF{)cvCJ+zDWDa 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≫;3biu5AQOvp{{09?ftaZ6A&UgfK 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 From dbad4b6ba89a05168be8eb702340f9887e1bf46a Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Mon, 25 Jul 2016 16:19:30 +0200 Subject: [PATCH 05/39] Use promises in API --- bin/cli.js | 107 ++++++++++++++++++++++-------------------- lib/graph.js | 8 ++-- lib/madge.js | 65 ++++++++++++++++--------- package.json | 1 + test/amd.js | 99 ++++++++++++++++++++++++--------------- test/cjs.js | 97 +++++++++++++++++++++++--------------- test/es6.js | 112 +++++++++++++++++++++++++++----------------- test/files/cjs/a.js | 2 + test/files/cjs/b.js | 1 + test/files/cjs/c.js | 0 test/madge.js | 71 ++++++++++++++++++++-------- 11 files changed, 346 insertions(+), 217 deletions(-) create mode 100644 test/files/cjs/a.js create mode 100644 test/files/cjs/b.js create mode 100644 test/files/cjs/c.js diff --git a/bin/cli.js b/bin/cli.js index 48a74ec8..9455b165 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -55,56 +55,61 @@ if (!program.color) { config.edgeColor = '#757575'; } -const res = madge(program.args[0], config); - -if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot && !program.json)) { - printResult.list(res.obj(), { - colors: program.color, - output: program.output - }); -} - -if (program.summary) { - printResult.summary(res.obj(), { - colors: program.color, - output: program.output - }); -} - -if (program.json) { - process.stdout.write(JSON.stringify(res.tree) + '\n'); -} - -if (program.circular) { - const circular = res.circular(); - - printResult.circular(circular, { - colors: program.color, - output: program.output - }); - - if (circular.length) { - process.exit(1); - } -} - -if (program.depends) { - printResult.depends(res.depends(program.depends), { - colors: program.color, - output: program.output - }); -} - -if (program.image) { - res.image((image) => { - fs.writeFile(program.image, image, (err) => { - if (err) { - throw err; +madge(program.args[0], config) + .then((res) => { + if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot && !program.json)) { + printResult.list(res.obj(), { + colors: program.color, + output: program.output + }); + } + + if (program.summary) { + printResult.summary(res.obj(), { + colors: program.color, + output: program.output + }); + } + + if (program.json) { + process.stdout.write(JSON.stringify(res.tree) + '\n'); + } + + if (program.circular) { + const circular = res.circular(); + + printResult.circular(circular, { + colors: program.color, + output: program.output + }); + + if (circular.length) { + process.exit(1); } - }); + } + + if (program.depends) { + printResult.depends(res.depends(program.depends), { + colors: program.color, + output: program.output + }); + } + + if (program.image) { + return res.image().then((image) => { + fs.writeFile(program.image, image, (err) => { + if (err) { + throw err; + } + }); + }); + } + + if (program.dot) { + process.stdout.write(res.dot()); + } + }) + .catch((err) => { + console.log(err); + process.exit(1); }); -} - -if (program.dot) { - process.stdout.write(res.dot()); -} diff --git a/lib/graph.js b/lib/graph.js index 58d76847..2c2b767c 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -55,9 +55,9 @@ function createGraphvizOptions(config) { * Creates a PNG image from the module dependency graph. * @param {Object} modules * @param {Object} config - * @param {Function} callback + * @return {Promise} */ -module.exports.image = function (modules, config, callback) { +module.exports.image = function (modules, config) { const g = graphviz.digraph('G'); const nodes = {}; @@ -85,7 +85,9 @@ module.exports.image = function (modules, config, callback) { }); }); - g.output(createGraphvizOptions(config), callback); + return new Promise((resolve) => { + g.output(createGraphvizOptions(config), resolve); + }); }; /** diff --git a/lib/madge.js b/lib/madge.js index b3ef3aff..ee1330cb 100644 --- a/lib/madge.js +++ b/lib/madge.js @@ -1,7 +1,7 @@ 'use strict'; -const fs = require('fs'); const path = require('path'); +const fs = require('mz/fs'); const dependencyTree = require('dependency-tree'); const cyclic = require('./cyclic'); const graph = require('./graph'); @@ -27,7 +27,7 @@ class Madge { * Class constructor. * @constructor * @api public - * @param {String|Object} filename + * @param {String} filename * @param {Object} config */ constructor(filename, config) { @@ -38,26 +38,43 @@ class Madge { this.config = Object.assign({}, defaultConfig, config); debug('using config', this.config); + this.filename = filename; this.excludeRegex = this.config.exclude ? new RegExp(this.config.exclude) : false; this.rootDirectory = this.config.directory ? path.resolve(this.config.directory) : path.dirname(filename); + } - if (typeof filename === 'object') { - this.tree = filename; - } else { - if (!fs.statSync(filename).isFile()) { // eslint-disable-line no-sync - throw new Error('Directory not supported'); - } - - this.tree = this.convertDependencyTree(dependencyTree({ - filename: filename, - directory: this.rootDirectory, - requireConfig: this.config.requireConfig, - webpackConfig: this.config.webpackConfig, - filter: this.pathFilter.bind(this) - })); - } - - this.sortDependencies(); + /** + * Will start parsing filename and compute dependencies. + * @return {Promise} + */ + parse() { + return fs + .exists(this.filename) + .then((exists) => { + if (!exists) { + throw new Error('Filename ' + this.filename + ' does not exists'); + } + + return fs.stat(this.filename); + }) + .then((stats) => { + if (!stats.isFile()) { + throw new Error('Filename ' + this.filename + ' is not a file'); + } + }) + .then(() => { + this.tree = this.convertDependencyTree(dependencyTree({ + filename: this.filename, + directory: this.rootDirectory, + requireConfig: this.config.requireConfig, + webpackConfig: this.config.webpackConfig, + filter: this.pathFilter.bind(this) + })); + + this.sortDependencies(); + + return this; + }); } /** @@ -176,11 +193,13 @@ class Madge { /** * Return the module dependency graph as a PNG image. * @api public - * @param {Function} callback + * @return {Promise} */ - image(callback) { - graph.image(this.tree, this.config, callback); + image() { + return graph.image(this.tree, this.config); } } -module.exports = (filename, config) => new Madge(filename, config); +module.exports = (filename, config) => { + return new Madge(filename, config).parse(); +}; diff --git a/package.json b/package.json index 9caba727..db8d378d 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "debug": "^2.2.0", "dependency-tree": "^5.4.1", "graphviz": "^0.0.8", + "mz": "^2.4.0", "rc": "^1.1.6" }, "devDependencies": { diff --git a/test/amd.js b/test/amd.js index a51cb3ae..cb4a2a4a 100644 --- a/test/amd.js +++ b/test/amd.js @@ -7,65 +7,88 @@ require('should'); describe('AMD', () => { const dir = __dirname + '/files/amd'; - it('should find recursive dependencies', () => { - madge(dir + '/ok/a.js').obj().should.eql({ - 'a': ['sub/b'], - 'sub/b': ['sub/c'], - 'sub/c': ['d'], - 'd': [] - }); + it('should find recursive dependencies', (done) => { + madge(dir + '/ok/a.js').then((res) => { + res.obj().should.eql({ + 'a': ['sub/b'], + 'sub/b': ['sub/c'], + 'sub/c': ['d'], + 'd': [] + }); + done(); + }).catch(done); }); - it('should ignore plugins', () => { - madge(dir + '/plugin.js').obj().should.eql({ - 'plugin': ['ok/d'], - 'ok/d': [] - }); + it('should ignore plugins', (done) => { + madge(dir + '/plugin.js').then((res) => { + res.obj().should.eql({ + 'plugin': ['ok/d'], + 'ok/d': [] + }); + done(); + }).catch(done); }); - it('should find nested dependencies', () => { - madge(dir + '/nested/main.js').obj().should.eql({ - 'a': [], - 'b': [], - 'main': [ - 'a', - 'b' - ] - }); + it('should find nested dependencies', (done) => { + madge(dir + '/nested/main.js').then((res) => { + res.obj().should.eql({ + 'a': [], + 'b': [], + 'main': [ + 'a', + 'b' + ] + }); + done(); + }).catch(done); }); - it('should find circular dependencies', () => { - madge(dir + '/circular/main.js').circular().should.eql([ - ['a', 'c'], - ['f', 'g', 'h'] - ]); + it('should find circular dependencies', (done) => { + madge(dir + '/circular/main.js').then((res) => { + res.circular().should.eql([ + ['a', 'c'], + ['f', 'g', 'h'] + ]); + done(); + }).catch(done); }); - it('should find circular dependencies with relative paths', () => { - madge(dir + '/circularRelative/a.js').circular().should.eql([['a', 'foo/b']]); + it('should find circular dependencies with relative paths', (done) => { + madge(dir + '/circularRelative/a.js').then((res) => { + res.circular().should.eql([['a', 'foo/b']]); + done(); + }).catch(done); }); - it('should find circular dependencies with alias', () => { + it('should find circular dependencies with alias', (done) => { madge(dir + '/circularAlias/dos.js', { requireConfig: dir + '/circularAlias/config.js' - }).circular().should.eql([['dos', 'x86']]); + }).then((res) => { + res.circular().should.eql([['dos', 'x86']]); + done(); + }).catch(done); }); - it('should work for files with ES6 code inside', () => { - madge(dir + '/amdes6.js') - .obj().should.eql({ + it('should work for files with ES6 code inside', (done) => { + madge(dir + '/amdes6.js').then((res) => { + res.obj().should.eql({ 'amdes6': ['ok/d'], 'ok/d': [] }); + done(); + }).catch(done); }); - it('should use paths found in RequireJS config', () => { + it('should use paths found in RequireJS config', (done) => { madge(dir + '/requirejs/a.js', { requireConfig: dir + '/requirejs/config.js' - }).obj().should.eql({ - 'a': ['vendor/jquery-2.0.3'], - 'vendor/jquery-2.0.3': [] - }); + }).then((res) => { + res.obj().should.eql({ + 'a': ['vendor/jquery-2.0.3'], + 'vendor/jquery-2.0.3': [] + }); + done(); + }).catch(done); }); it.skip('should compile coffeescript on-the-fly', () => { diff --git a/test/cjs.js b/test/cjs.js index 3467c316..0ce6d71a 100644 --- a/test/cjs.js +++ b/test/cjs.js @@ -7,60 +7,81 @@ require('should'); describe('CommonJS', () => { const dir = __dirname + '/files/cjs'; - it('should find recursive dependencies', () => { - madge(dir + '/normal/a.js').obj().should.eql({ - 'a': ['sub/b'], - 'd': [], - 'sub/b': ['sub/c'], - 'sub/c': ['d'] - }); + it('should find recursive dependencies', (done) => { + madge(dir + '/normal/a.js').then((res) => { + res.obj().should.eql({ + 'a': ['sub/b'], + 'd': [], + 'sub/b': ['sub/c'], + 'sub/c': ['d'] + }); + done(); + }).catch(done); }); - it('should handle paths outside directory', () => { - madge(dir + '/normal/sub/c.js').obj().should.eql({ - '../d': [], - 'c': ['../d'] - }); + it('should handle paths outside directory', (done) => { + madge(dir + '/normal/sub/c.js').then((res) => { + res.obj().should.eql({ + '../d': [], + 'c': ['../d'] + }); + done(); + }).catch(done); }); - it('should find circular dependencies', () => { - madge(dir + '/circular/a.js').circular().should.eql([ - ['a', 'b', 'c'] - ]); + it('should find circular dependencies', (done) => { + madge(dir + '/circular/a.js').then((res) => { + res.circular().should.eql([ + ['a', 'b', 'c'] + ]); + done(); + }).catch(done); }); - it('should exclude core modules by default', () => { - madge(dir + '/core.js').obj().should.eql({ - 'core': [] - }); + it('should exclude core modules by default', (done) => { + madge(dir + '/core.js').then((res) => { + res.obj().should.eql({ + 'core': [] + }); + done(); + }).catch(done); }); - it('should exclude NPM modules by default', () => { - madge(dir + '/npm.js').obj().should.eql({ - 'normal/d': [], - 'npm': ['normal/d'] - }); + it('should exclude NPM modules by default', (done) => { + madge(dir + '/npm.js').then((res) => { + res.obj().should.eql({ + 'normal/d': [], + 'npm': ['normal/d'] + }); + done(); + }).catch(done); }); - it('should be able to include NPM modules', () => { + it('should be able to include NPM modules', (done) => { madge(dir + '/npm.js', { includeNpm: true - }).obj().should.eql({ - 'node_modules/a': [], - 'normal/d': [], - 'npm': ['node_modules/a', 'normal/d'] - }); + }).then((res) => { + res.obj().should.eql({ + 'node_modules/a': [], + 'normal/d': [], + 'npm': ['node_modules/a', 'normal/d'] + }); + done(); + }).catch(done); }); - it('should be able to show file extensions', () => { + it('should be able to show file extensions', (done) => { madge(dir + '/normal/a.js', { showFileExtension: true - }).obj().should.eql({ - 'a.js': ['sub/b.js'], - 'd.js': [], - 'sub/b.js': ['sub/c.js'], - 'sub/c.js': ['d.js'] - }); + }).then((res) => { + res.obj().should.eql({ + 'a.js': ['sub/b.js'], + 'd.js': [], + 'sub/b.js': ['sub/c.js'], + 'sub/c.js': ['d.js'] + }); + done(); + }).catch(done); }); }); diff --git a/test/es6.js b/test/es6.js index 314224ad..bd031377 100644 --- a/test/es6.js +++ b/test/es6.js @@ -7,65 +7,89 @@ require('should'); describe('ES6', () => { const dir = __dirname + '/files/es6'; - it('should find circular dependencies', () => { - madge(dir + '/circular/a.js').circular().should.eql([ - ['a', 'b', 'c'] - ]); + it('should find circular dependencies', (done) => { + madge(dir + '/circular/a.js').then((res) => { + res.circular().should.eql([ + ['a', 'b', 'c'] + ]); + done(); + }).catch(done); }); - it('should tackle errors in files', () => { - madge(dir + '/error.js').obj().should.eql({ - 'error': [] - }); + it('should tackle errors in files', (done) => { + madge(dir + '/error.js').then((res) => { + res.obj().should.eql({ + 'error': [] + }); + done(); + }).catch(done); }); - it('should find absolute imports from the root', () => { - madge(dir + '/absolute.js').obj().should.eql({ - 'absolute': ['absolute/a'], - 'absolute/a': [] - }); + it('should find absolute imports from the root', (done) => { + madge(dir + '/absolute.js').then((res) => { + res.obj().should.eql({ + 'absolute': ['absolute/a'], + 'absolute/a': [] + }); + done(); + }).catch(done); }); - it('should find imports on files with ES7', () => { - madge(dir + '/async.js').obj().should.eql({ - 'absolute/b': [], - 'async': ['absolute/b'] - }); + it('should find imports on files with ES7', (done) => { + madge(dir + '/async.js').then((res) => { + res.obj().should.eql({ + 'absolute/b': [], + 'async': ['absolute/b'] + }); + done(); + }).catch(done); }); - it('should support export x from "./file"', () => { - madge(dir + '/re-export/c.js').obj().should.eql({ - 'a': [], - 'b-default': ['a'], - 'b-named': ['a'], - 'b-star': ['a'], - 'c': [ - 'b-default', - 'b-named', - 'b-star' - ] - }); + it('should support export x from "./file"', (done) => { + madge(dir + '/re-export/c.js').then((res) => { + res.obj().should.eql({ + 'a': [], + 'b-default': ['a'], + 'b-named': ['a'], + 'b-star': ['a'], + 'c': [ + 'b-default', + 'b-named', + 'b-star' + ] + }); + done(); + }).catch(done); }); - it('should find imports on files with JSX content', () => { - madge(dir + '/jsx.js').obj().should.eql({ - 'jsx': ['absolute/b'], - 'absolute/b': [] - }); + it('should find imports on files with JSX content', (done) => { + madge(dir + '/jsx.js').then((res) => { + res.obj().should.eql({ + 'jsx': ['absolute/b'], + 'absolute/b': [] + }); + done(); + }).catch(done); }); - it('should find import in JSX files', () => { - madge(dir + '/jsx/basic.jsx').obj().should.eql({ - 'basic': ['other'], - 'other': [] - }); + it('should find import in JSX files', (done) => { + madge(dir + '/jsx/basic.jsx').then((res) => { + res.obj().should.eql({ + 'basic': ['other'], + 'other': [] + }); + done(); + }).catch(done); }); - it('should be able to exclude modules', () => { + it('should be able to exclude modules', (done) => { madge(dir + '/normal/a.js', { exclude: '.*\/sub' - }).obj().should.eql({ - 'a': [] - }); + }).then((res) => { + res.obj().should.eql({ + 'a': [] + }); + done(); + }).catch(done); }); }); diff --git a/test/files/cjs/a.js b/test/files/cjs/a.js new file mode 100644 index 00000000..92fdfb39 --- /dev/null +++ b/test/files/cjs/a.js @@ -0,0 +1,2 @@ + require('./b'); + require('./c'); \ No newline at end of file diff --git a/test/files/cjs/b.js b/test/files/cjs/b.js new file mode 100644 index 00000000..426ce71f --- /dev/null +++ b/test/files/cjs/b.js @@ -0,0 +1 @@ +require('./c'); \ No newline at end of file diff --git a/test/files/cjs/c.js b/test/files/cjs/c.js new file mode 100644 index 00000000..e69de29b diff --git a/test/madge.js b/test/madge.js index 26fb8323..75c43954 100644 --- a/test/madge.js +++ b/test/madge.js @@ -5,36 +5,67 @@ const madge = require('../lib/madge'); require('should'); describe('Madge', () => { - describe('#constructor', () => { - it('should throw error on missing filename argument', () => { - (() => { - madge(); - }).should.throw('Filename argument is missing'); - }); + it('should throw error on missing filename argument', () => { + (() => { + madge(); + }).should.throw('Filename argument is missing'); + }); + + it('should return a Promise', () => { + madge(__dirname + '/files/cjs/a.js').should.be.Promise(); // eslint-disable-line new-cap }); + + it('should throw error if filename argument is not a file', (done) => { + madge(__dirname + '/files').catch((err) => { + err.message.should.match(/is not a file/); + done(); + }).catch(done); + }); + + it('should throw error if file does not exists', (done) => { + madge(__dirname + '/missing.js').catch((err) => { + err.message.should.match(/does not exists/); + done(); + }).catch(done); + }); + describe('#obj', () => { - it('should return dependency object', () => { - madge({a: ['b', 'c']}).obj().should.eql({a: ['b', 'c']}); + it('should return dependency object', (done) => { + madge(__dirname + '/files/cjs/a.js').then((res) => { + res.obj().should.eql({ + a: ['b', 'c'], + b: ['c'], + c: [] + }); + done(); + }).catch(done); }); }); describe('#dot', () => { - it('should be able to output graphviz DOT format', () => { - madge({ - a: ['b', 'c'], - b: ['c'], - c: [] - }).dot().should.eql('digraph G {\n "a";\n "b";\n "c";\n "a" -> "b";\n "a" -> "c";\n "b" -> "c";\n}\n'); + it('should be able to output graphviz DOT format', (done) => { + madge(__dirname + '/files/cjs/b.js').then((res) => { + res.dot().should.eql('digraph G {\n "b";\n "c";\n "b" -> "c";\n}\n'); + done(); + }).catch(done); }); }); describe('#depends', () => { - it('should return modules that depends on another', () => { - madge({ - a: ['b', 'c'], - b: ['c'], - c: [] - }).depends('c').should.eql(['a', 'b']); + it('should return modules that depends on another', (done) => { + madge(__dirname + '/files/cjs/a.js').then((res) => { + res.depends('c').should.eql(['a', 'b']); + done(); + }).catch(done); + }); + }); + + describe('#image', () => { + it('should return a Promise', (done) => { + madge(__dirname + '/files/cjs/a.js').then((res) => { + res.image('c').should.be.Promise(); // eslint-disable-line new-cap + done(); + }).catch(done); }); }); }); From fa2891ecef04bd5f1286d1db68a33ef96c0cd123 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Thu, 28 Jul 2016 19:20:49 +0200 Subject: [PATCH 06/39] Rename files in /lib --- bin/cli.js | 19 +++++++++++-------- examples/madge.png | Bin 33793 -> 33034 bytes lib/{madge.js => api.js} | 2 +- lib/{print.js => output.js} | 10 ++++++++++ package.json | 8 ++++---- test/amd.js | 2 +- test/{madge.js => api.js} | 2 +- test/cjs.js | 2 +- test/es6.js | 2 +- 9 files changed, 30 insertions(+), 17 deletions(-) rename lib/{madge.js => api.js} (100%) rename lib/{print.js => output.js} (92%) rename test/{madge.js => api.js} (97%) diff --git a/bin/cli.js b/bin/cli.js index 9455b165..613eb382 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -2,12 +2,12 @@ 'use strict'; const fs = require('fs'); -const version = require('../package.json').version; const program = require('commander'); -const printResult = require('../lib/print'); -const madge = require('../lib/madge'); const rc = require('rc')('madge'); const debug = require('debug')('madge'); +const version = require('../package.json').version; +const output = require('../lib/output'); +const madge = require('../lib/api'); program .version(version) @@ -58,14 +58,14 @@ if (!program.color) { madge(program.args[0], config) .then((res) => { if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot && !program.json)) { - printResult.list(res.obj(), { + output.list(res.obj(), { colors: program.color, output: program.output }); } if (program.summary) { - printResult.summary(res.obj(), { + output.summary(res.obj(), { colors: program.color, output: program.output }); @@ -78,7 +78,7 @@ madge(program.args[0], config) if (program.circular) { const circular = res.circular(); - printResult.circular(circular, { + output.circular(circular, { colors: program.color, output: program.output }); @@ -89,7 +89,7 @@ madge(program.args[0], config) } if (program.depends) { - printResult.depends(res.depends(program.depends), { + output.depends(res.depends(program.depends), { colors: program.color, output: program.output }); @@ -110,6 +110,9 @@ madge(program.args[0], config) } }) .catch((err) => { - console.log(err); + output.error(err, { + colors: program.color + }); + process.exit(1); }); diff --git a/examples/madge.png b/examples/madge.png index 86f644304de5b4ed9aea75d9232bf5f6b1c7a41d..f662a48bb774a1a7aa9b9b413f9e92aa0e65363a 100644 GIT binary patch literal 33034 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

kU^ zv;N5U($wub3g`5g{$6JuC!wHOF^}Qb+CbS;d=Z~l2R(o#a8-iw_~_i}jcrEUDDl%* z25Z^itG6#3|Gb+^_3d9m5$>fnnVn?mfc2(M7s#Y{3Ge05BZfzP6)#*>d#w*nP~kXZ zDI5>2PhVVG>+U|;Z8Xl!oX>-VxL4m>p+6rxnO?&BK^>^t7@<^u10&QcIZ(ikk=qYcyZWJFc0CN zu(T{LH|vdLR~KY6B}7oSzMDcP6$Bv;h(dR_w$U(L(E*(1B>ge(^w2&Lj=GkJ)2TQd zGo1_y@o2GO@}JXON97RJ^?gUK8(ffQ1JbbR^HM!+uHNS7=yKMLBx(Sw8=I_|ah?@4 z^Y4GLv0tKa_;Xz#Qe@)+?#b%(04m{(4GilumR5J=CKo+PGc;ABt@?u2X&-(Jc6jS( zrOv%efH3%J>lTYJ%Q9Kb=GMfl*B%*+55REDv=y7hXAK6Ey_4uVlxU+Cvb%HQ2GTAW`zAd#Q^x&#kTkgf2GLs>#a#8wr%&8-0r1H5KvBFgV!88KO=Ql zry`QmctZy;M^NT>n3i|YE!)s$gL4FRr?oEC)LRF{ZS_45VBfTcvz$)hutz zq*51bNwj(Uy0SN(YSDkW4ZJl={IVD>jJ+>A*kUkft9)F^o>@Y8TJz|#(khW=%yY%^aPL(>C|2(<2w7`x{sF6>#Z)#h5bYCTy0zt*1l2L+vZ zaePOP|9XEs`_h)?N4z&Seii`@1MHn5iy_TUA<%u){`@G1&q;g^M@=f7}N*VgrME5BK+dmyP6Nbnjn zm^^ei;jTZ*y6_6t+sFIow=(f|^0iG9rD!@z&koh7(aGV7DMNG%HD)!8{C_U-*|+P|eXl0yq}khS5W_39VK?qw{ek3-HOS z(=2F`NTw-o@_t~??lW?Yo*{ZFbUy4HGGPXG*gWhP>)iA0?crqeJir_>jTJ+wc%G2! z0-kLqN7(<#J{N@XfkuPHCftO5|y>+r>w z%fF579cT?z$YJVtpxHa&R`qxW@lOOh;`6p~0W2~3bGWWNcRo#Wx2UP6|z? zZ=Ryplq^(022`+-V6i49cUzM~8FiizP|AE;v$?a`ev)mnw5Jhh+5xYjqC56-=$`u< z{CnYR9DXod)xWLqV?Tk3+&fELhyB-R#<_f3=WSZ6>zddjTnD={{(GQJ3<0l3*5zP; z)RjSlN*<4*lJpT@@zrKoqsxfZ-`D~ga`M$5SHoDn>IUW^+*9C?0%|Y#0?i?ucG-&S zZFS7UA#899VnyAb}Vl!}B3pteQ*pYXAf+@@h_DT={dg!3*$kAc4&`HBL`h*y6EXJrlfrCUC=s_1pjw9U+(euxU3R@t@;s;5=h%fC}12ZZLipcIZs1j_Szx7`5=J)$Kfkp}e<1@0BUu~wZc4jj%TwruH zK-mgg(q);FX~^uVfP&Zs?;JxjLrcieYl584sTG~^5J`dTUnv%634 zQA+`Do;GeGpjYl+@-@C`az6%TeK0+f#Eh*d@$|xrKuVMem!5-un?M zwo3H|FzflIwp#>*-|#VCTa|jDXHHH>;$Ej;?pU+CDn1Hc4 z3h?KQ$^UBoeT%_qy6CK`?r9ev?hlOF%v3ApGsv%QP{8Ymt;=8Es=O<^(>z)6_$*?g z>=nBksh4)AZ=#J@?EL^ctaE$Hf1m`j_1Yalx`x1QRZRWH^t!Oxw4QprjEHxvGG|iu zuYw3`VL`DkkD9QiNEJ|xm^#SEXM{-hy8je+KTan|qj{vu$)pzlpwsqG9R2Urua(g> zxvRI9JN{>DTz+r#%>c)O|2h~bWYr1_Bb!vxV&$!hmMA(6g9FL`s`Ez-f-6rKzG676q861MJjM+ZOh=ml}Tvg=jKT*EM z7pr{z*_@YyzXbmfxCi)O*#{?->+)r8boxtfTEsvs+Bwo?H=fXOio673Dgq1nyyA5} zS(=tJqeB94rbbc|;}|KwS|g);e<1$)V>2=PS@l;fv?o7UYL({)b@FV5f93-w-iKWs zEHQnlDGCwboGe(_h`&EzesTwOocVfe@HiE@nl;V$-_lin5tElA2Qq(wgD4qRKw<7) z5Xu1hb!5lY>=}*Yw|_OJD*?h&tNef8n{4EVsQJBmUlzbPMvq6c@}jowO&0Xo{hksQ z^XpDEtM+YZ2{(Ese?G(CP+E%Pb1`g9LA622BfX|msZGh>*m?zh-^en>vYdU}RGp-p zf*;Hksdj2|x=YIlkmH!%pO?Du(n-ZN%7zV>esEpA=-@nRr~`&Sc(A*upHSuhE4keL zxhE2We8swA=8g5#YpE=p=7uKB#4<$Q(IjgcZwVfXSW;wlghyVW9pvhEMUE3z1E*miGZZtY(FeJ|?WwS}v zNwwxd=IvDzaL<&cH%_K~Dy#mWP(pQ66G#Afn`W8f9*A|Vy z{qBIw!CA=H@BYV=c_G$KBMKJZ9p}>(8QmrGw@C>Mvs--jk|qU| z_UOjX2Nj|mc4!5hEP$8%>nJane;Cz$WteA=dxRnQj}@fB`b`1RixC+=;?KdAM0edM zP#{ml_fq5BRZNwbF>GeK;->&A($&VvB0LIaiK<+5?m|U-+A}?~pEn^E-W){*UKg^; zx7&>#Y~0nb`;&u}L7cyOfLmL%&uF z=b^fUV3Onu)ZG$Q?er=q0+~wtf)WU3Aej9J*U>vr5=bWLUhPI<372lZvR@{vPFV{{ z^2?r2sAlT)>l7mF+*9)rdGQniN`q!VkI>Mr=5N_Tg(V|ACFNQSkNufPh~4) z$QMKDE)6ETCJ^zxjoMVi=WAYOt=%%m%oRte51qWG=~9NlMpC216LFVJAAQ~XlxgFe z)}dhH-b6CxFVHs&1!kh0u}-7>Q4piXTKR_zvs%^-5Iu?Bk_ln^^<`y4m1phkos%Oe z^5#mgC}I_2Tnuptw}mIk*P>b6KHpugljL-5(nKV}xhy0uqNdK#eJLbn$7kZteI@i=^8HZv!X#%&8_=8)^%6FY)mM1jI^>$>5X`ZQ5LYMg+x0S!Iw*0tbMunU)`?6D|h>KQN zvC|AN-8sX+$|6Lsw8y}or{T97J5^K3m#!qiotc%r-=sf68F+=~K`aKq(Os58a zc!FN|H=7E5+>!6rIeV6GrpvAs-$}H{_PY%_I_@@FB~&yNA@hY&!nHI=iQRdlIE0~U zi7vEUgr+QS*4Y&Q*mClm?v6yi!8Y+NhuOnf8i0I z=YG!1_f7WM20qz~ZJCE5DcVrg%nu?Ef_ts9k#7s!44E*A6Gt7l5sO`YI4}>KY)1_m zTlY9a@@9g_E=vBnmY}M3gW6m$3QR}p1o+?e+hs{tpQ46eceo9rJ#vFOepaU?o?LZa zN7ET}b3R8e*-xfJqJh6xP&8VlaFJ}`N#u^sG3*U`O4{2>U$ow<0 zx1LRCkY~sE+K7rd?QC~IUEu#H_FN0~A$gmr)P4H8t3S=>zp)7Bo5v+imd3q*DpCqE zit|lAGLG4vm0&M$(`7^E-^A}gGe29F;{~)5|B=U?6)@gVEUf*LE)DxuT*&Yw^R^o` zYNro^VyMos(xdQ%?3=^~bParWk{*jdp5cfapOf-`*m@ zpenzNO3~32+|C+JEdMA!!^=)RD(q__{%5)DDH`d)m0@1twuVI;R%K)Zf6r-g-UBBW zliZ&2^1C!o0~y)i5}tSrr6pn6om-8#h0SUyIx7F1y?%jaOZv`E+^Gg6rPPl9q^P)t zc7!icG3RK+!)z|#0HxVXmXA8K65npW=H|2QYoqRJ#o5b(M=6n#mf-~x()a}qIrItsjl%YAF z!4+^IDWI)EQ8o;Rjp4Qu`DeL#OIkA2e+7B42TUyVyMWSS@BSH z30@nfX;gepp_Q#&bfn*pUsrfB-0hNM>SVy~d*=MnzD#lWTrF5V$?MF>=UbUXBCYj# z^lGZ}*8Jky$zyyi;}NaJayp96E;OD9@$wA0+j95loF`@PbEvUFv5Z6#?oWZHLo0>t z^tz)}uUGqDe{EWeEJ#P`g4B%pXy|){2Mu5)Vlv_m`Xq~_&I{LN%gdc^dGE#(tPhG& z;q|=yb%<82HsB95FvGY8^$ts-vdwI~+*g+0T{|41ym=JMq*kTUJ)4L0W6!$) zwiPnpD9_;ic5G2KBWt%L(oCb=+wR06yvQDE(WMXF#vCA4`%}X1jZ?!$*Wr^}s0v3E zfOEzQyF5;%T(0p6PR|l4!U4nt_i<48@)3?2uubxpu?l|nW!W`bg3&UK(}GMUC97W; zeJZciBrXn_2U|`pJclSA$hWqd>||Q}$(2484H0E!6;UJytfYdm3nV zN)Z!EL%!~>%p?@yckreDLn=PHKLC-viNJt}58Z+|Kjo-btwDvnqzr+MaBQa`-2jla zlqUoz-FJnT&NK@(d%d#rcbn^TQKh=P4Ab~UdeWEDq&|#TZIU$d9zl%`K@hm^g*a>h zO_DvyQ~Sg=c}4gI{yf?jRKb`8lTWdDUs=(H>wRGmkWQ0>pdo@JT}}^xLuB4wmE_x# zLy_>zQu)(1o~S3Xvzfkt?~Mv}o!-wdfiK(WaTxr)gMi*L8r5Dd#pRS0F9f z`SJ+y;+y!ESodXNU2_xlrbBq#VGoOb72S*wYNO;YrIG}iT#FD`W~?8r)_t9vUlBbh zEOSoQ=#-IYB13CZuWuvbj>_7$XC+G_=5_g;f}jcivQ zeEoNp*GJ`UsJIGW0pW7mo*;kq2)Bd%#4_E_+0id9;}@W;Q}EkzP8BpN=ipXSZ8f#c zN7!IyWj3z1>xX_BbqcexTf_<*7eU1>xEqPol zuYYgh$pUO&h;&OG&c|ve#zbiNEbf z%?LI5Rv`9!Aj6%kOrmX82E&56a&tC4r=>a@ssYj0PdU`maJvt*&EIC)gc+Qi>WZ5; zJ>gfRW$oYjCV~y-0~^a&eCef_Dy16lDxC!bw!@(} zysc5_9{i+?E#&-Uy?6iVAt2r|)!w#N9m{hkkDpEq@8wLUE4|2lp<1T&cJANt0(o1b zhF=~BK_;_F8GKh;5}@&qE#{HMe*`?Sd6Pz`l{5^cyU{pwxxJA!4Z~F_7t-nd+pS`2 zX|A~U0*i?6Gq%Yv-l2=7^gklpcI~z*i%N(4lNBCXh4?fd!j=3JDxL%zsM>-4@|>EO zOTvf{hI~=~kExZG)-<|$OYvyF)*SPg?qu1NI04$?`-{aDj@1Df#`ukvPcAb>Ic)~b z{|Na*9{dy(pQF0tum{@JH%R3_-m(6M)6*-zU7JWF&2fP5 zAr@PF&;89#3XK}g75~2qDJD9<#oahU@|r(<5HJ6x2xM%xy_BB^(fwd*)M)4Q4Q9o( zPT*4=hpb;5pfR2*0`-8*&(J4om4(*vU*2tr$8o%-q7AAxxJ%i*tzGVWHDBRHQ-dap z{dM5UZd6WOyP0IHSllfI`**o-DAiKR_K+00+&sO`Z-P3U33s}E5N%3onFmk3lJ@>) zTK1E^6`Pj)2y*>)lTG(Ta@Q#w8~L41tao;vBj7R9W*wFM7eX`M& zBN`!!U=gc5Csti2V3Qpjf+p=6=}DP%c~c9WIU9wR+zD} zS=pE^SLCg28qpEmhqR;S-U?peYdl#^%$So8nwPY_voi@{$`MkThTZ*z6yl@9>4RIu zze?M#ld+1@Rkru@5u9DKZ+E_h}Z3d0k`_WY8%e+cbRC4&a8i7(x8Mrp_`bkEUtcK+xcx;O-XO z-5r9vgy0f_yK8WFcXxM(;O-XO-QiofpSS9(%0DK%BRxI6J$)V*d|4O%tvu5rt^(5x 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 diff --git a/lib/madge.js b/lib/api.js similarity index 100% rename from lib/madge.js rename to lib/api.js index ee1330cb..c8df2dc3 100644 --- a/lib/madge.js +++ b/lib/api.js @@ -2,10 +2,10 @@ const path = require('path'); const fs = require('mz/fs'); +const debug = require('debug')('madge'); const dependencyTree = require('dependency-tree'); const cyclic = require('./cyclic'); const graph = require('./graph'); -const debug = require('debug')('madge'); const defaultConfig = { showFileExtension: false, diff --git a/lib/print.js b/lib/output.js similarity index 92% rename from lib/print.js rename to lib/output.js index e82eeb50..59d24dcd 100644 --- a/lib/print.js +++ b/lib/output.js @@ -110,3 +110,13 @@ module.exports.depends = function (modules, opts) { console.log(c(id, 'grey', opts.colors)); }); }; + +/** + * Print error to the console. + * @param {Object} err + * @param {Object} opts + * @return {undefined} + */ +module.exports.error = function (err, opts) { + console.log(c(err, 'red', opts.colors)); +}; diff --git a/package.json b/package.json index db8d378d..2e891576 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,10 @@ "engines": { "node": ">=4.x.x" }, + "main": "./lib/api", + "bin": { + "madge": "./bin/cli.js" + }, "scripts": { "test": "npm run lint && npm run madge && npm run mocha", "mocha": "mocha test/*.js", @@ -47,9 +51,5 @@ "mocha": "^2.3.3", "release-it": "^2.4.0", "should": "^9.0.2" - }, - "main": "./lib/madge", - "bin": { - "madge": "./bin/cli.js" } } diff --git a/test/amd.js b/test/amd.js index cb4a2a4a..65dc5eff 100644 --- a/test/amd.js +++ b/test/amd.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -const madge = require('../lib/madge'); +const madge = require('../lib/api'); require('should'); describe('AMD', () => { diff --git a/test/madge.js b/test/api.js similarity index 97% rename from test/madge.js rename to test/api.js index 75c43954..cae7d29d 100644 --- a/test/madge.js +++ b/test/api.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -const madge = require('../lib/madge'); +const madge = require('../lib/api'); require('should'); describe('Madge', () => { diff --git a/test/cjs.js b/test/cjs.js index 0ce6d71a..895fef41 100644 --- a/test/cjs.js +++ b/test/cjs.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -const madge = require('../lib/madge'); +const madge = require('../lib/api'); require('should'); describe('CommonJS', () => { diff --git a/test/es6.js b/test/es6.js index bd031377..b8680cb9 100644 --- a/test/es6.js +++ b/test/es6.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -const madge = require('../lib/madge'); +const madge = require('../lib/api'); require('should'); describe('ES6', () => { From 73c323d2bc652c6cb7d35451ab3efe68856f96f8 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 11:18:13 +0200 Subject: [PATCH 07/39] Overall cleanup --- README.md | 26 ++++++++-------- bin/cli.js | 10 +++---- lib/api.js | 82 +++++++++++++++++++++++++++------------------------ lib/graph.js | 14 ++++----- lib/output.js | 16 +++++----- test/amd.js | 31 ------------------- 6 files changed, 76 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 789a5c34..e33104a5 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ [![NPM Status](http://img.shields.io/npm/dm/madge.svg?style=flat-square)](https://www.npmjs.org/package/madge) [![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](https://paypal.me/pahen) -Can be used for creating graphs from your dependencies or find circular dependencies in your code. The dependencies are calculated using Joel Kemp's awesome [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree). +A tool for generating a visual graph from your module dependencies. Can also find circular dependencies and give you other useful info about your dependencies. The dependencies are generated using Joel Kemp's awesome [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree). Works for JS (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus); basically, any filetype supported by [precinct](https://github.com/mrjoelkemp/node-precinct). - - For CommonJS modules, 3rd party dependencies (npm installed dependencies) are included in the tree by default + - For CommonJS modules, 3rd party dependencies (npm installed dependencies) are exluded in the tree - Dependency path resolutions are handled by [filing-cabinet](https://github.com/mrjoelkemp/node-filing-cabinet) - Supports RequireJS and Webpack loaders - All core Node modules (assert, path, fs, etc) are removed from the dependency list by default @@ -37,7 +37,7 @@ Here's an example generated from the madge source using the command `madge bin/c ## Graphviz (optional) -Only required if you want to generate the visual graphs using [Graphviz](http://www.graphviz.org/). +> Only required if you want to generate the visual graphs using [Graphviz](http://www.graphviz.org/). ### Mac OS X @@ -49,7 +49,7 @@ Only required if you want to generate the visual graphs using [Graphviz](http:// # API -## madge(filename: string, config: object) +## madge(filePath: string, config: object) > `config` is optional and should be [configuration](#configuration) to be used. @@ -123,8 +123,8 @@ Property | Type | Default | Description `fontSize` | String | 14px | Font size to use in graph `backgroundColor` | String | #000000 | Background color for the graph `nodeColor` | String | #c6c5fe | The default node color to use in the graph -`noDependenciesColor` | String | #cfffac | Color to use for nodes with dependencies -`circularDependencyColor` | String | #ff6c60 | The color to used for circular dependencies +`noDependencyColor` | String | #cfffac | The color to use for nodes with no dependencies +`cyclicNodeColor` | String | #ff6c60 | The color to used for circular dependencies `edgeColor` | String | #757575 | The edge color to use in the graph > Note that when running the CLI it's possible to use a runtime configuration file. The config should placed in `.madgerc` in your project or home folder. Look [here](https://github.com/dominictarr/rc#standards) for alternative locations for the file. Here's an example: @@ -138,33 +138,33 @@ Property | Type | Default | Description ## Examples -### List all module dependencies +> List all module dependencies $ madge path/src/app.js -### Finding circular dependencies +> Finding circular dependencies $ madge --circular path/src/app.js -### Show modules that depends on a given module +> Show modules that depends on a given module $ madge --depends 'wheels' path/src/app.js -### Excluding modules +> Excluding modules $ madge --exclude '^foo$|^bar$|^tests' path/src/app.js -### Save graph as a PNG image (graphviz required) +> Save graph as a PNG image (graphviz required) $ madge --image graph.png path/src/app.js -### Save graph as a [DOT](http://en.wikipedia.org/wiki/DOT_language) file for further processing (graphviz required) +> Save graph as a [DOT](http://en.wikipedia.org/wiki/DOT_language) file for further processing (graphviz required) $ madge --dot path/src/app.js > graph.gv # Debugging -To enable debugging output if you encounter problems, run madge in the following way +> To enable debugging output if you encounter problems, run madge in the following way $ DEBUG=* madge path/src/app.js diff --git a/bin/cli.js b/bin/cli.js index 613eb382..a4a9a758 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -17,8 +17,8 @@ program .option('--summary', 'show summary of all dependencies') .option('--json', 'show list of dependencies as JSON') .option('--circular', 'show circular dependencies') - .option('--depends', 'show modules that depends on the given id') - .option('--image ', 'write graph to file as a PNG image') + .option('--depends ', 'show modules that depends on the given id') + .option('--image ', 'write graph to file as a PNG image') .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') .option('--no-color', 'disable color in output and image', false) @@ -50,8 +50,8 @@ delete config.configs; if (!program.color) { config.backgroundColor = '#ffffff'; config.nodeColor = '#00000'; - config.noDependenciesColor = '#00000'; - config.circularDependencyColor = '#000000'; + config.noDependencyColor = '#00000'; + config.cyclicNodeColor = '#000000'; config.edgeColor = '#757575'; } @@ -72,7 +72,7 @@ madge(program.args[0], config) } if (program.json) { - process.stdout.write(JSON.stringify(res.tree) + '\n'); + process.stdout.write(JSON.stringify(res.obj()) + '\n'); } if (program.circular) { diff --git a/lib/api.js b/lib/api.js index c8df2dc3..0308ebb7 100644 --- a/lib/api.js +++ b/lib/api.js @@ -17,8 +17,8 @@ const defaultConfig = { fontSize: '14px', backgroundColor: '#000000', nodeColor: '#c6c5fe', - noDependenciesColor: '#cfffac', - circularDependencyColor: '#ff6c60', + noDependencyColor: '#cfffac', + cyclicNodeColor: '#ff6c60', edgeColor: '#757575' }; @@ -27,20 +27,20 @@ class Madge { * Class constructor. * @constructor * @api public - * @param {String} filename + * @param {String} filePath * @param {Object} config */ - constructor(filename, config) { - if (!filename) { + constructor(filePath, config) { + if (!filePath) { throw new Error('Filename argument is missing'); } this.config = Object.assign({}, defaultConfig, config); debug('using config', this.config); - this.filename = filename; + this.filePath = filePath; this.excludeRegex = this.config.exclude ? new RegExp(this.config.exclude) : false; - this.rootDirectory = this.config.directory ? path.resolve(this.config.directory) : path.dirname(filename); + this.baseDir = this.config.directory ? path.resolve(this.config.directory) : path.dirname(filePath); } /** @@ -49,23 +49,23 @@ class Madge { */ parse() { return fs - .exists(this.filename) + .exists(this.filePath) .then((exists) => { if (!exists) { - throw new Error('Filename ' + this.filename + ' does not exists'); + throw new Error('Filename ' + this.filePath + ' does not exists'); } - return fs.stat(this.filename); + return fs.stat(this.filePath); }) .then((stats) => { if (!stats.isFile()) { - throw new Error('Filename ' + this.filename + ' is not a file'); + throw new Error('Filename ' + this.filePath + ' is not a file'); } }) .then(() => { this.tree = this.convertDependencyTree(dependencyTree({ - filename: this.filename, - directory: this.rootDirectory, + filename: this.filePath, + directory: this.baseDir, requireConfig: this.config.requireConfig, webpackConfig: this.config.webpackConfig, filter: this.pathFilter.bind(this) @@ -95,17 +95,19 @@ class Madge { convertDependencyTree(tree, graph) { graph = graph || {}; - Object.keys(tree).forEach((key) => { - const id = this.processPath(key); + Object + .keys(tree) + .forEach((key) => { + const id = this.processPath(key); - if (!graph[id]) { - graph[id] = Object - .keys(tree[key]) - .map((dep) => this.processPath(dep)); - } + if (!graph[id]) { + graph[id] = Object + .keys(tree[key]) + .map((dep) => this.processPath(dep)); + } - this.convertDependencyTree(tree[key], graph); - }); + this.convertDependencyTree(tree[key], graph); + }); return graph; } @@ -116,7 +118,7 @@ class Madge { * @return {String} */ processPath(absPath) { - absPath = path.relative(this.rootDirectory, absPath); + absPath = path.relative(this.baseDir, absPath); if (!this.config.showFileExtension) { absPath = absPath.replace(/\.\w+$/, ''); @@ -129,10 +131,13 @@ class Madge { * Sort dependencies by name. */ sortDependencies() { - this.tree = Object.keys(this.tree).sort().reduce((acc, id) => { - (acc[id] = this.tree[id]).sort(); - return acc; - }, {}); + this.tree = Object + .keys(this.tree) + .sort() + .reduce((acc, id) => { + acc[id] = this.tree[id].sort(); + return acc; + }, {}); } /** @@ -169,16 +174,9 @@ class Madge { * @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 Object + .keys(this.tree) + .filter((module) => this.tree[module].indexOf(id) >= 0); } /** @@ -200,6 +198,12 @@ class Madge { } } -module.exports = (filename, config) => { - return new Madge(filename, config).parse(); +/** + * Expose API. + * @param {String} filePath + * @param {Object} config + * @return {Promise} + */ +module.exports = (filePath, config) => { + return new Madge(filePath, config).parse(); }; diff --git a/lib/graph.js b/lib/graph.js index 2c2b767c..af3d8085 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -33,16 +33,16 @@ function checkGraphvizInstalled() { */ function createGraphvizOptions(config) { return { - 'type': 'png', - 'G': { + type: 'png', + G: { overlap: false, layout: config.layout, bgcolor: config.backgroundColor }, - 'E': { + E: { color: config.edgeColor }, - 'N': { + N: { fontname: config.fontName, fontsize: config.fontSize, color: config.nodeColor, @@ -69,16 +69,16 @@ module.exports.image = function (modules, config) { nodes[id] = nodes[id] || g.addNode(id); if (!modules[id].length) { - setNodeColor(nodes[id], config.noDependenciesColor); + setNodeColor(nodes[id], config.noDependencyColor); } else if (cyclicModules.indexOf(id) >= 0) { - setNodeColor(nodes[id], config.circularDependencyColor); + setNodeColor(nodes[id], config.cyclicNodeColor); } modules[id].forEach((depId) => { nodes[depId] = nodes[depId] || g.addNode(depId); if (!modules[depId]) { - setNodeColor(nodes[depId], config.noDependenciesColor); + setNodeColor(nodes[depId], config.noDependencyColor); } g.addEdge(nodes[id], nodes[depId]); diff --git a/lib/output.js b/lib/output.js index 59d24dcd..4efdf1c7 100644 --- a/lib/output.js +++ b/lib/output.js @@ -3,7 +3,7 @@ require('colors'); /** - * Return colored string (or not). + * Return colored string. * @param {String} str * @param {String} name * @param {Boolean} use @@ -14,12 +14,12 @@ function c(str, name, use) { } /** - * Return the given object as JSON. + * Print given object as JSON. * @param {Object} obj * @return {String} */ -function toJSON(obj) { - return JSON.stringify(obj, null, ' ') + '\n'; +function printJSON(obj) { + return console.log(JSON.stringify(obj, null, ' ')); } /** @@ -32,7 +32,7 @@ module.exports.list = function (modules, opts) { opts = opts || {}; if (opts.output === 'json') { - return process.stdout.write(toJSON(modules)); + return printJSON(modules); } Object.keys(modules).forEach((id) => { @@ -65,7 +65,7 @@ module.exports.summary = function (modules, opts) { }); if (opts.output === 'json') { - return process.stdout.write(toJSON(o)); + return printJSON(o); } }; @@ -77,7 +77,7 @@ module.exports.summary = function (modules, opts) { */ module.exports.circular = function (circular, opts) { if (opts.output === 'json') { - return process.stdout.write(toJSON(circular)); + return printJSON(circular); } if (!circular.length) { @@ -103,7 +103,7 @@ module.exports.circular = function (circular, opts) { */ module.exports.depends = function (modules, opts) { if (opts.output === 'json') { - return process.stdout.write(toJSON(modules)); + return printJSON(modules); } modules.forEach((id) => { diff --git a/test/amd.js b/test/amd.js index 65dc5eff..a0f9b05f 100644 --- a/test/amd.js +++ b/test/amd.js @@ -90,35 +90,4 @@ describe('AMD', () => { done(); }).catch(done); }); - - it.skip('should compile coffeescript on-the-fly', () => { - madge(dir + '/coffeescript/a.coffee').obj().should.eql({ - 'a': ['b'], 'b': [] - }); - }); - - it.skip('should handle optimized files', () => { - madge(dir + '/a-built.js').obj().should.eql( - {'a': ['sub/b'], 'd': [], 'sub/b': ['sub/c'], 'sub/c': ['d'] - }); - }); - - it.skip('should handle optimized files originating with a `require` call', () => { - madge(dir + '/b-built.js').obj().should.eql({ - '': ['sub/b'], - 'a': [], - 'd': [], - 'sub/b': ['sub/c'], 'sub/c': ['d'] - }); - }); - - it.skip('should handle optimized files originating with a `require` call and a designated main module', () => { - madge(dir + '/b-built.js', { - mainRequireModule: 'a' - }).obj().should.eql({ - 'a': ['sub/b'], - 'd': [], - 'sub/b': ['sub/c'], 'sub/c': ['d'] - }); - }); }); From d2fefb3b8575ebd75470811210ad30f19e049b1a Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 11:24:36 +0200 Subject: [PATCH 08/39] Create CHANGELOG --- CHANGELOG.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 107 +-------------------------------------------------- 2 files changed, 105 insertions(+), 106 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f8184773 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,104 @@ +# CHANGELOG + +## 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). + +## v0.5.4 (June 13, 2016) +* Improved JSX and ES7 support (Thanks to Joel Kemp). + +## v0.5.3 (November 25, 2015) +* Correct regex on CommonJS parser to detect a core module (Thanks to Guillaume Gomez). + +## v0.5.2 (October 16, 2015) +* Updated dependency resolve to latest version. + +## v0.5.1 (October 15, 2015) +* Updated dependencies to newer versions (Thanks to Martin Kapp). + +## v0.5.0 (April 2, 2015) +* Added support for ES6 modules (Thanks to Marc Laval). +* Added support for setting custom file extension name (Thanks to Marc Laval). + +## v0.4.1 (December 19, 2014) +* Fixed issues with absolute paths for modules IDs in Windows (all tests should now pass on Windows too). + +## v0.4.0 (December 19, 2014) +* Add support for JSX (React) and additional module paths (Thanks to Ben Lowery). +* Fix for detecting presence of AMD or CommonJS modules (Thanks to Aaron Russ). +* Now resolves the module IDs from the RequireJS paths-config properly (Thanks to russaa). +* Added support for option findNestedDependencies to find nested dependencies in AMD modules. + +## v0.3.5 (Septemper 22, 2014) +* Fix issue with number of graph node lines increased with each render (Thanks to Colin H. Fredericks). + +## v0.3.4 (Septemper 04, 2014) +* Correctly detect circular dependencies when using path aliases in RequireJS config (Thanks to Nicolas Ramz). + +## v0.3.3 (July 11, 2014) +* Fixed bug with relative paths in AMD not handled properly when checking for cyclic dependencies. + +## v0.3.2 (June 25, 2014) +* Handle anonymous require() as entry in the RequireJS optimized file (Thanks to Benjamin Horsleben). + +## v0.3.1 (June 03, 2014) +* Apply exclude to RequireJS shim dependencies (Thanks to Michael White). + +## v0.3.0 (May 25, 2014) +* Added support for onParseFile and onAddModule options (Thanks to Brandon Selway). +* Added JSON output option (Thanks to Drew Foehn). +* Fix for optimized files including dependency information for excluded modules (Thanks to Drew Foehn). Fixes [issue](https://github.com/pahen/madge/issues/26). + +## v0.2.0 (April 17, 2014) +* Added support for including shim dependencies found in RequiredJS config (specify with option -R). + +## v0.1.9 (February 17, 2014) +* Ensure forward slashes are used in modules paths (Windows). + +## v0.1.8 (January 27, 2014) +* Added support for reading AMD dependencies from a r.js optimized file by using option -O. + +## v0.1.7 (September 20, 2013) +* Added missing fontsize option when generating images. + +## v0.1.6 (September 04, 2013) +* AMD plugins are now ignored as dependencies. Fixes [issue](https://github.com/pahen/grunt-madge/issues/1). + +## v0.1.5 (September 04, 2013) +* Fixed Windows [issue](https://github.com/pahen/madge/issues/17) when reading from standard input with --read. + +## v0.1.4 (January 10, 2013) +* Switched library for walking directory tree which should solve issues on [Windows](https://github.com/pahen/madge/issues/8). + +## v0.1.3 (December 28, 2012) +* Added proper exit code when running "madge --circular" so it can be used in build scripts. + +## v0.1.2 (November 15, 2012) +* Relative AMD module identifiers (if the first term is "." or "..") are now resolved. + +## v0.1.1 (September 3, 2012) +* Tweaked circular dependency path output. + +## v0.1.0 (September 3, 2012) +* Complete path in circular dependencies is now printed (and marked as red in image graphs). + +## v0.0.5 (August 8, 2012) +* Added support for CoffeeScript. Files with extension .coffee will automatically be compiled on-the-fly. + +## v0.0.4 (August 17, 2012) +* Fixed dependency issues with Node.js v0.8. + +## v0.0.3 (July 01, 2012) +* Added support for Node.js v0.8 and dropped support for lower versions. + +## v0.0.2 (May 21, 2012) +* Added ability to read config file and customize colors. + +## v0.0.1 (May 20, 2012) +* Initial release. \ No newline at end of file diff --git a/README.md b/README.md index e33104a5..41f02c00 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Works for JS (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus); - Supports RequireJS and Webpack loaders - All core Node modules (assert, path, fs, etc) are removed from the dependency list by default -See [release notes](#release-notes) for latest changes. +See [CHANGELOG](CHANGELOG.md) for latest changes. ## Examples Here's a very simple example of a generated image. @@ -195,111 +195,6 @@ minimize a global energy function, which is equivalent to statistical multi-dime * **circo** circular layout, after Six and Tollis 99, Kauffman and Wiese 02. This is suitable for certain diagrams of multiple cyclic structures, such as certain telecommunications networks. -# Release Notes - -## 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). - -## v0.5.4 (June 13, 2016) -* Improved JSX and ES7 support (Thanks to Joel Kemp). - -## v0.5.3 (November 25, 2015) -* Correct regex on CommonJS parser to detect a core module (Thanks to Guillaume Gomez). - -## v0.5.2 (October 16, 2015) -* Updated dependency resolve to latest version. - -## v0.5.1 (October 15, 2015) -* Updated dependencies to newer versions (Thanks to Martin Kapp). - -## v0.5.0 (April 2, 2015) -* Added support for ES6 modules (Thanks to Marc Laval). -* Added support for setting custom file extension name (Thanks to Marc Laval). - -## v0.4.1 (December 19, 2014) -* Fixed issues with absolute paths for modules IDs in Windows (all tests should now pass on Windows too). - -## v0.4.0 (December 19, 2014) -* Add support for JSX (React) and additional module paths (Thanks to Ben Lowery). -* Fix for detecting presence of AMD or CommonJS modules (Thanks to Aaron Russ). -* Now resolves the module IDs from the RequireJS paths-config properly (Thanks to russaa). -* Added support for option findNestedDependencies to find nested dependencies in AMD modules. - -## v0.3.5 (Septemper 22, 2014) -* Fix issue with number of graph node lines increased with each render (Thanks to Colin H. Fredericks). - -## v0.3.4 (Septemper 04, 2014) -* Correctly detect circular dependencies when using path aliases in RequireJS config (Thanks to Nicolas Ramz). - -## v0.3.3 (July 11, 2014) -* Fixed bug with relative paths in AMD not handled properly when checking for cyclic dependencies. - -## v0.3.2 (June 25, 2014) -* Handle anonymous require() as entry in the RequireJS optimized file (Thanks to Benjamin Horsleben). - -## v0.3.1 (June 03, 2014) -* Apply exclude to RequireJS shim dependencies (Thanks to Michael White). - -## v0.3.0 (May 25, 2014) -* Added support for onParseFile and onAddModule options (Thanks to Brandon Selway). -* Added JSON output option (Thanks to Drew Foehn). -* Fix for optimized files including dependency information for excluded modules (Thanks to Drew Foehn). Fixes [issue](https://github.com/pahen/madge/issues/26). - -## v0.2.0 (April 17, 2014) -* Added support for including shim dependencies found in RequiredJS config (specify with option -R). - -## v0.1.9 (February 17, 2014) -* Ensure forward slashes are used in modules paths (Windows). - -## v0.1.8 (January 27, 2014) -* Added support for reading AMD dependencies from a r.js optimized file by using option -O. - -## v0.1.7 (September 20, 2013) -* Added missing fontsize option when generating images. - -## v0.1.6 (September 04, 2013) -* AMD plugins are now ignored as dependencies. Fixes [issue](https://github.com/pahen/grunt-madge/issues/1). - -## v0.1.5 (September 04, 2013) -* Fixed Windows [issue](https://github.com/pahen/madge/issues/17) when reading from standard input with --read. - -## v0.1.4 (January 10, 2013) -* Switched library for walking directory tree which should solve issues on [Windows](https://github.com/pahen/madge/issues/8). - -## v0.1.3 (December 28, 2012) -* Added proper exit code when running "madge --circular" so it can be used in build scripts. - -## v0.1.2 (November 15, 2012) -* Relative AMD module identifiers (if the first term is "." or "..") are now resolved. - -## v0.1.1 (September 3, 2012) -* Tweaked circular dependency path output. - -## v0.1.0 (September 3, 2012) -* Complete path in circular dependencies is now printed (and marked as red in image graphs). - -## v0.0.5 (August 8, 2012) -* Added support for CoffeeScript. Files with extension .coffee will automatically be compiled on-the-fly. - -## v0.0.4 (August 17, 2012) -* Fixed dependency issues with Node.js v0.8. - -## v0.0.3 (July 01, 2012) -* Added support for Node.js v0.8 and dropped support for lower versions. - -## v0.0.2 (May 21, 2012) -* Added ability to read config file and customize colors. - -## v0.0.1 (May 20, 2012) -* Initial release. - # License MIT License \ No newline at end of file From 4138eecd7d54b89c53ee760031b25607b0a72098 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 12:27:42 +0200 Subject: [PATCH 09/39] Move writing of image file from CLI to API --- README.md | 9 +++------ bin/cli.js | 9 +-------- lib/api.js | 11 ++++++++--- lib/graph.js | 35 ++++++++++++++++++++++++++--------- test/api.js | 42 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 75 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 41f02c00..2603ac6f 100644 --- a/README.md +++ b/README.md @@ -97,17 +97,14 @@ Returns a `Promise` resolved with the Madge instance object. console.log(res.dot()); }); -#### .image() +#### .image(imagePath: string) -> Returns a `Promise` resolved with an image representation of the module dependency graph. +> Write the graph as an image to the given image path. Returns a `Promise`. const madge = require('madge'); madge('path/to/app.js') - .then((res) => res.image()) - .then((image) => { - // write image to file - }); + .then((res) => res.image('path/to/image.png')) }); # Configuration diff --git a/bin/cli.js b/bin/cli.js index a4a9a758..5a67c124 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,7 +1,6 @@ #!/usr/bin/env node 'use strict'; -const fs = require('fs'); const program = require('commander'); const rc = require('rc')('madge'); const debug = require('debug')('madge'); @@ -96,13 +95,7 @@ madge(program.args[0], config) } if (program.image) { - return res.image().then((image) => { - fs.writeFile(program.image, image, (err) => { - if (err) { - throw err; - } - }); - }); + return res.image(program.image); } if (program.dot) { diff --git a/lib/api.js b/lib/api.js index 0308ebb7..ab645d36 100644 --- a/lib/api.js +++ b/lib/api.js @@ -189,12 +189,17 @@ class Madge { } /** - * Return the module dependency graph as a PNG image. + * Write dependency graph to image. * @api public + * @param {String} imagePath * @return {Promise} */ - image() { - return graph.image(this.tree, this.config); + image(imagePath) { + if (!imagePath) { + return Promise.reject(new Error('imagePath not provided')); + } + + return graph.image(this.tree, imagePath, this.config); } } diff --git a/lib/graph.js b/lib/graph.js index af3d8085..254ff220 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -1,5 +1,6 @@ 'use strict'; +const fs = require('mz/fs'); const exec = require('child_process').exec; const cyclic = require('./cyclic'); const graphviz = require('graphviz'); @@ -52,17 +53,15 @@ function createGraphvizOptions(config) { } /** - * Creates a PNG image from the module dependency graph. - * @param {Object} modules - * @param {Object} config + * Creates the graphviz graph. + * @param {Object} modules + * @param {Object} config + * @param {Object} options * @return {Promise} */ -module.exports.image = function (modules, config) { +function createGraph(modules, config, options) { const g = graphviz.digraph('G'); const nodes = {}; - - checkGraphvizInstalled(); - const cyclicModules = cyclic(modules).reduce((a, b) => a.concat(b), []); Object.keys(modules).forEach((id) => { @@ -85,9 +84,27 @@ module.exports.image = function (modules, config) { }); }); - return new Promise((resolve) => { - g.output(createGraphvizOptions(config), resolve); + return new Promise((resolve, reject) => { + g.output(options, resolve, (code, out, err) => { + reject(err); + }); }); +} + +/** + * Creates an image from the module dependency graph. + * @param {Object} modules + * @param {String} imagePath + * @param {Object} config + * @return {Promise} + */ +module.exports.image = function (modules, imagePath, config) { + checkGraphvizInstalled(); + + const options = createGraphvizOptions(config); + + return createGraph(modules, config, options) + .then((image) => fs.writeFile(imagePath, image)); }; /** diff --git a/test/api.js b/test/api.js index cae7d29d..418fc66a 100644 --- a/test/api.js +++ b/test/api.js @@ -1,7 +1,11 @@ /* eslint-env mocha */ 'use strict'; +const os = require('os'); +const path = require('path'); +const fs = require('mz/fs'); const madge = require('../lib/api'); + require('should'); describe('Madge', () => { @@ -61,11 +65,39 @@ describe('Madge', () => { }); describe('#image', () => { - it('should return a Promise', (done) => { - madge(__dirname + '/files/cjs/a.js').then((res) => { - res.image('c').should.be.Promise(); // eslint-disable-line new-cap - done(); - }).catch(done); + let imagePath; + + beforeEach(() => { + imagePath = path.join(os.tmpdir(), 'madge_' + Date.now() + '_image.png'); + }); + + afterEach(() => { + fs.unlink(imagePath); + }); + + it('rejects if a filename is not supplied', (done) => { + madge(__dirname + '/files/cjs/a.js') + .then((res) => res.image()) + .catch((err) => { + err.message.should.eql('imagePath not provided'); + done(); + }); + }); + + it('writes image to file', (done) => { + madge(__dirname + '/files/cjs/a.js') + .then((res) => res.image(imagePath)) + .then(() => { + return fs + .exists(imagePath) + .then((exists) => { + if (!exists) { + throw new Error(imagePath + ' not created'); + } + done(); + }); + }) + .catch(done); }); }); }); From 1f161199c1f889350b41596e01c800b338efe72a Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 13:23:19 +0200 Subject: [PATCH 10/39] Return promise for API -> dot() --- README.md | 10 ++++++---- bin/cli.js | 4 +++- lib/api.js | 2 +- lib/graph.js | 40 ++++++++++++++++++++-------------------- test/api.js | 17 ++++++++++------- 5 files changed, 40 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 2603ac6f..5df4e824 100644 --- a/README.md +++ b/README.md @@ -89,13 +89,15 @@ Returns a `Promise` resolved with the Madge instance object. #### .dot() -> Returns a `String` with a DOT representation of the module dependency graph. +> Returns a `Promise` resolved with a DOT representation of the module dependency graph. const madge = require('madge'); - madge('path/to/app.js').then((res) => { - console.log(res.dot()); - }); + madge('path/to/app.js') + .then((res) => res.dot()) + .then((output) => { + console.log(output; + }); #### .image(imagePath: string) diff --git a/bin/cli.js b/bin/cli.js index 5a67c124..dc853c87 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -99,7 +99,9 @@ madge(program.args[0], config) } if (program.dot) { - process.stdout.write(res.dot()); + return res.dot().then((output) => { + process.stdout.write(output); + }); } }) .catch((err) => { diff --git a/lib/api.js b/lib/api.js index ab645d36..30ef2109 100644 --- a/lib/api.js +++ b/lib/api.js @@ -182,7 +182,7 @@ class Madge { /** * Return the module dependency graph as DOT output. * @api public - * @return {String} + * @return {Promise} */ dot() { return graph.dot(this.tree); diff --git a/lib/graph.js b/lib/graph.js index 254ff220..0a7e09c1 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -1,7 +1,7 @@ 'use strict'; const fs = require('mz/fs'); -const exec = require('child_process').exec; +const exec = require('mz/child_process').exec; const cyclic = require('./cyclic'); const graphviz = require('graphviz'); @@ -17,13 +17,11 @@ function setNodeColor(node, color) { /** * Check if Graphviz is installed on the system. - * @throws Error + * @return {Promise} */ function checkGraphvizInstalled() { - exec('gvpr -V', (error, stdout, stderr) => { - if (error !== null) { - throw new Error('Graphviz could not be found. Ensure that "gvpr" is in your $PATH.\n' + error); - } + return exec('gvpr -V').catch((error) => { + throw new Error('Graphviz could not be found. Ensure that "gvpr" is in your $PATH.\n' + error); }); } @@ -99,33 +97,35 @@ function createGraph(modules, config, options) { * @return {Promise} */ module.exports.image = function (modules, imagePath, config) { - checkGraphvizInstalled(); - const options = createGraphvizOptions(config); - return createGraph(modules, config, options) - .then((image) => fs.writeFile(imagePath, image)); + return checkGraphvizInstalled() + .then(() => { + return createGraph(modules, config, options) + .then((image) => fs.writeFile(imagePath, image)); + }); }; /** * Return the module dependency graph as DOT output. * @param {Object} modules - * @return {String} + * @return {Promise} */ module.exports.dot = function (modules) { const nodes = {}; const g = graphviz.digraph('G'); - checkGraphvizInstalled(); + return checkGraphvizInstalled() + .then(() => { + Object.keys(modules).forEach((id) => { + nodes[id] = nodes[id] || g.addNode(id); - Object.keys(modules).forEach((id) => { - nodes[id] = nodes[id] || g.addNode(id); + modules[id].forEach((depId) => { + nodes[depId] = nodes[depId] || g.addNode(depId); + g.addEdge(nodes[id], nodes[depId]); + }); + }); - modules[id].forEach((depId) => { - nodes[depId] = nodes[depId] || g.addNode(depId); - g.addEdge(nodes[id], nodes[depId]); + return g.to_dot(); }); - }); - - return g.to_dot(); }; diff --git a/test/api.js b/test/api.js index 418fc66a..a0eca0cb 100644 --- a/test/api.js +++ b/test/api.js @@ -47,11 +47,14 @@ describe('Madge', () => { }); describe('#dot', () => { - it('should be able to output graphviz DOT format', (done) => { - madge(__dirname + '/files/cjs/b.js').then((res) => { - res.dot().should.eql('digraph G {\n "b";\n "c";\n "b" -> "c";\n}\n'); - done(); - }).catch(done); + it('should return a promise resolved with graphviz DOT output', (done) => { + madge(__dirname + '/files/cjs/b.js') + .then((res) => res.dot()) + .then((output) => { + output.should.eql('digraph G {\n "b";\n "c";\n "b" -> "c";\n}\n'); + done(); + }) + .catch(done); }); }); @@ -75,7 +78,7 @@ describe('Madge', () => { fs.unlink(imagePath); }); - it('rejects if a filename is not supplied', (done) => { + it('should reject if a filename is not supplied', (done) => { madge(__dirname + '/files/cjs/a.js') .then((res) => res.image()) .catch((err) => { @@ -84,7 +87,7 @@ describe('Madge', () => { }); }); - it('writes image to file', (done) => { + it('should write image to file', (done) => { madge(__dirname + '/files/cjs/a.js') .then((res) => res.image(imagePath)) .then(() => { From c472c2c23999e33810093bb95fe484c1b9f61b78 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 13:30:27 +0200 Subject: [PATCH 11/39] =?UTF-8?q?Remove=20=E2=80=98should=E2=80=99=20from?= =?UTF-8?q?=20each=20test=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/amd.js | 16 ++++++++-------- test/api.js | 18 +++++++++--------- test/cjs.js | 14 +++++++------- test/es6.js | 16 ++++++++-------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/test/amd.js b/test/amd.js index a0f9b05f..a08f2f85 100644 --- a/test/amd.js +++ b/test/amd.js @@ -7,7 +7,7 @@ require('should'); describe('AMD', () => { const dir = __dirname + '/files/amd'; - it('should find recursive dependencies', (done) => { + it('finds recursive dependencies', (done) => { madge(dir + '/ok/a.js').then((res) => { res.obj().should.eql({ 'a': ['sub/b'], @@ -19,7 +19,7 @@ describe('AMD', () => { }).catch(done); }); - it('should ignore plugins', (done) => { + it('ignores plugins', (done) => { madge(dir + '/plugin.js').then((res) => { res.obj().should.eql({ 'plugin': ['ok/d'], @@ -29,7 +29,7 @@ describe('AMD', () => { }).catch(done); }); - it('should find nested dependencies', (done) => { + it('finds nested dependencies', (done) => { madge(dir + '/nested/main.js').then((res) => { res.obj().should.eql({ 'a': [], @@ -43,7 +43,7 @@ describe('AMD', () => { }).catch(done); }); - it('should find circular dependencies', (done) => { + it('finds circular dependencies', (done) => { madge(dir + '/circular/main.js').then((res) => { res.circular().should.eql([ ['a', 'c'], @@ -53,14 +53,14 @@ describe('AMD', () => { }).catch(done); }); - it('should find circular dependencies with relative paths', (done) => { + it('finds circular dependencies with relative paths', (done) => { madge(dir + '/circularRelative/a.js').then((res) => { res.circular().should.eql([['a', 'foo/b']]); done(); }).catch(done); }); - it('should find circular dependencies with alias', (done) => { + it('finds circular dependencies with alias', (done) => { madge(dir + '/circularAlias/dos.js', { requireConfig: dir + '/circularAlias/config.js' }).then((res) => { @@ -69,7 +69,7 @@ describe('AMD', () => { }).catch(done); }); - it('should work for files with ES6 code inside', (done) => { + it('works for files with ES6 code inside', (done) => { madge(dir + '/amdes6.js').then((res) => { res.obj().should.eql({ 'amdes6': ['ok/d'], @@ -79,7 +79,7 @@ describe('AMD', () => { }).catch(done); }); - it('should use paths found in RequireJS config', (done) => { + it('uses paths found in RequireJS config', (done) => { madge(dir + '/requirejs/a.js', { requireConfig: dir + '/requirejs/config.js' }).then((res) => { diff --git a/test/api.js b/test/api.js index a0eca0cb..ea598c3e 100644 --- a/test/api.js +++ b/test/api.js @@ -9,24 +9,24 @@ const madge = require('../lib/api'); require('should'); describe('Madge', () => { - it('should throw error on missing filename argument', () => { + it('throws error on missing filename argument', () => { (() => { madge(); }).should.throw('Filename argument is missing'); }); - it('should return a Promise', () => { + it('returns a Promise', () => { madge(__dirname + '/files/cjs/a.js').should.be.Promise(); // eslint-disable-line new-cap }); - it('should throw error if filename argument is not a file', (done) => { + it('throws error if filename argument is not a file', (done) => { madge(__dirname + '/files').catch((err) => { err.message.should.match(/is not a file/); done(); }).catch(done); }); - it('should throw error if file does not exists', (done) => { + it('throws error if file does not exists', (done) => { madge(__dirname + '/missing.js').catch((err) => { err.message.should.match(/does not exists/); done(); @@ -34,7 +34,7 @@ describe('Madge', () => { }); describe('#obj', () => { - it('should return dependency object', (done) => { + it('returns dependency object', (done) => { madge(__dirname + '/files/cjs/a.js').then((res) => { res.obj().should.eql({ a: ['b', 'c'], @@ -47,7 +47,7 @@ describe('Madge', () => { }); describe('#dot', () => { - it('should return a promise resolved with graphviz DOT output', (done) => { + it('returns a promise resolved with graphviz DOT output', (done) => { madge(__dirname + '/files/cjs/b.js') .then((res) => res.dot()) .then((output) => { @@ -59,7 +59,7 @@ describe('Madge', () => { }); describe('#depends', () => { - it('should return modules that depends on another', (done) => { + it('returns modules that depends on another', (done) => { madge(__dirname + '/files/cjs/a.js').then((res) => { res.depends('c').should.eql(['a', 'b']); done(); @@ -78,7 +78,7 @@ describe('Madge', () => { fs.unlink(imagePath); }); - it('should reject if a filename is not supplied', (done) => { + it('rejects if a filename is not supplied', (done) => { madge(__dirname + '/files/cjs/a.js') .then((res) => res.image()) .catch((err) => { @@ -87,7 +87,7 @@ describe('Madge', () => { }); }); - it('should write image to file', (done) => { + it('writes image to file', (done) => { madge(__dirname + '/files/cjs/a.js') .then((res) => res.image(imagePath)) .then(() => { diff --git a/test/cjs.js b/test/cjs.js index 895fef41..ef8871cc 100644 --- a/test/cjs.js +++ b/test/cjs.js @@ -7,7 +7,7 @@ require('should'); describe('CommonJS', () => { const dir = __dirname + '/files/cjs'; - it('should find recursive dependencies', (done) => { + it('finds recursive dependencies', (done) => { madge(dir + '/normal/a.js').then((res) => { res.obj().should.eql({ 'a': ['sub/b'], @@ -19,7 +19,7 @@ describe('CommonJS', () => { }).catch(done); }); - it('should handle paths outside directory', (done) => { + it('handles path outside directory', (done) => { madge(dir + '/normal/sub/c.js').then((res) => { res.obj().should.eql({ '../d': [], @@ -29,7 +29,7 @@ describe('CommonJS', () => { }).catch(done); }); - it('should find circular dependencies', (done) => { + it('finds circular dependencies', (done) => { madge(dir + '/circular/a.js').then((res) => { res.circular().should.eql([ ['a', 'b', 'c'] @@ -38,7 +38,7 @@ describe('CommonJS', () => { }).catch(done); }); - it('should exclude core modules by default', (done) => { + it('excludes core modules by default', (done) => { madge(dir + '/core.js').then((res) => { res.obj().should.eql({ 'core': [] @@ -47,7 +47,7 @@ describe('CommonJS', () => { }).catch(done); }); - it('should exclude NPM modules by default', (done) => { + it('excludes NPM modules by default', (done) => { madge(dir + '/npm.js').then((res) => { res.obj().should.eql({ 'normal/d': [], @@ -57,7 +57,7 @@ describe('CommonJS', () => { }).catch(done); }); - it('should be able to include NPM modules', (done) => { + it('can include NPM modules', (done) => { madge(dir + '/npm.js', { includeNpm: true }).then((res) => { @@ -70,7 +70,7 @@ describe('CommonJS', () => { }).catch(done); }); - it('should be able to show file extensions', (done) => { + it('can show file extensions', (done) => { madge(dir + '/normal/a.js', { showFileExtension: true }).then((res) => { diff --git a/test/es6.js b/test/es6.js index b8680cb9..a56c57f6 100644 --- a/test/es6.js +++ b/test/es6.js @@ -7,7 +7,7 @@ require('should'); describe('ES6', () => { const dir = __dirname + '/files/es6'; - it('should find circular dependencies', (done) => { + it('finds circular dependencies', (done) => { madge(dir + '/circular/a.js').then((res) => { res.circular().should.eql([ ['a', 'b', 'c'] @@ -16,7 +16,7 @@ describe('ES6', () => { }).catch(done); }); - it('should tackle errors in files', (done) => { + it('tackles error in files', (done) => { madge(dir + '/error.js').then((res) => { res.obj().should.eql({ 'error': [] @@ -25,7 +25,7 @@ describe('ES6', () => { }).catch(done); }); - it('should find absolute imports from the root', (done) => { + it('finds absolute imports from the root', (done) => { madge(dir + '/absolute.js').then((res) => { res.obj().should.eql({ 'absolute': ['absolute/a'], @@ -35,7 +35,7 @@ describe('ES6', () => { }).catch(done); }); - it('should find imports on files with ES7', (done) => { + it('finds imports on files with ES7', (done) => { madge(dir + '/async.js').then((res) => { res.obj().should.eql({ 'absolute/b': [], @@ -45,7 +45,7 @@ describe('ES6', () => { }).catch(done); }); - it('should support export x from "./file"', (done) => { + it('supports export x from "./file"', (done) => { madge(dir + '/re-export/c.js').then((res) => { res.obj().should.eql({ 'a': [], @@ -62,7 +62,7 @@ describe('ES6', () => { }).catch(done); }); - it('should find imports on files with JSX content', (done) => { + it('finds imports on files with JSX content', (done) => { madge(dir + '/jsx.js').then((res) => { res.obj().should.eql({ 'jsx': ['absolute/b'], @@ -72,7 +72,7 @@ describe('ES6', () => { }).catch(done); }); - it('should find import in JSX files', (done) => { + it('finds import in JSX files', (done) => { madge(dir + '/jsx/basic.jsx').then((res) => { res.obj().should.eql({ 'basic': ['other'], @@ -82,7 +82,7 @@ describe('ES6', () => { }).catch(done); }); - it('should be able to exclude modules', (done) => { + it('can exclude modules', (done) => { madge(dir + '/normal/a.js', { exclude: '.*\/sub' }).then((res) => { From b9832dedd59f7a7e03c8fadcbd695f7ea1f1007a Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 14:08:18 +0200 Subject: [PATCH 12/39] =?UTF-8?q?Replace=20=E2=80=9C=E2=80=94output=20json?= =?UTF-8?q?=E2=80=9D=20with=20=E2=80=9C=E2=80=94json=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/cli.js | 16 ++++++---------- lib/output.js | 10 +++++----- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index dc853c87..0bdb10c6 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -14,9 +14,9 @@ program .option('--directory ', '') .option('--list', 'show list of all dependencies (default)') .option('--summary', 'show summary of all dependencies') - .option('--json', 'show list of dependencies as JSON') .option('--circular', 'show circular dependencies') .option('--depends ', 'show modules that depends on the given id') + .option('--json', 'show output as JSON') .option('--image ', 'write graph to file as a PNG image') .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') @@ -56,30 +56,26 @@ if (!program.color) { madge(program.args[0], config) .then((res) => { - if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot && !program.json)) { + if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot)) { output.list(res.obj(), { colors: program.color, - output: program.output + json: program.json }); } if (program.summary) { output.summary(res.obj(), { colors: program.color, - output: program.output + json: program.json }); } - if (program.json) { - process.stdout.write(JSON.stringify(res.obj()) + '\n'); - } - if (program.circular) { const circular = res.circular(); output.circular(circular, { colors: program.color, - output: program.output + json: program.json }); if (circular.length) { @@ -90,7 +86,7 @@ madge(program.args[0], config) if (program.depends) { output.depends(res.depends(program.depends), { colors: program.color, - output: program.output + json: program.json }); } diff --git a/lib/output.js b/lib/output.js index 4efdf1c7..16347b0d 100644 --- a/lib/output.js +++ b/lib/output.js @@ -31,7 +31,7 @@ function printJSON(obj) { module.exports.list = function (modules, opts) { opts = opts || {}; - if (opts.output === 'json') { + if (opts.json) { return printJSON(modules); } @@ -57,14 +57,14 @@ module.exports.summary = function (modules, opts) { Object.keys(modules).sort((a, b) => { return modules[b].length - modules[a].length; }).forEach((id) => { - if (opts.output === 'json') { + if (opts.json) { o[id] = modules[id].length; } else { console.log(c(id + ': ', 'grey', opts.colors) + c(modules[id].length, 'cyan', opts.colors)); } }); - if (opts.output === 'json') { + if (opts.json) { return printJSON(o); } }; @@ -76,7 +76,7 @@ module.exports.summary = function (modules, opts) { * @return {undefined} */ module.exports.circular = function (circular, opts) { - if (opts.output === 'json') { + if (opts.json) { return printJSON(circular); } @@ -102,7 +102,7 @@ module.exports.circular = function (circular, opts) { * @return {undefined} */ module.exports.depends = function (modules, opts) { - if (opts.output === 'json') { + if (opts.json) { return printJSON(modules); } From 44a10ebea47a455e570a7c523d88a35ba3f91424 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 14:25:30 +0200 Subject: [PATCH 13/39] Determine image format to use from file extension --- README.md | 5 +++-- lib/graph.js | 6 ++++-- test/api.js | 9 +++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5df4e824..6c0a1f46 100644 --- a/README.md +++ b/README.md @@ -101,14 +101,15 @@ Returns a `Promise` resolved with the Madge instance object. #### .image(imagePath: string) -> Write the graph as an image to the given image path. Returns a `Promise`. +> Write the graph as an image to the given image path. The [image format](http://www.graphviz.org/content/output-formats) to use is determined from the file extension. Returns a `Promise`. const madge = require('madge'); madge('path/to/app.js') - .then((res) => res.image('path/to/image.png')) + .then((res) => res.image('path/to/image.svg')) }); + # Configuration Property | Type | Default | Description diff --git a/lib/graph.js b/lib/graph.js index 0a7e09c1..ab4188df 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -1,5 +1,6 @@ 'use strict'; +const path = require('path'); const fs = require('mz/fs'); const exec = require('mz/child_process').exec; const cyclic = require('./cyclic'); @@ -32,7 +33,6 @@ function checkGraphvizInstalled() { */ function createGraphvizOptions(config) { return { - type: 'png', G: { overlap: false, layout: config.layout, @@ -84,7 +84,7 @@ function createGraph(modules, config, options) { return new Promise((resolve, reject) => { g.output(options, resolve, (code, out, err) => { - reject(err); + reject(new Error(err)); }); }); } @@ -99,6 +99,8 @@ function createGraph(modules, config, options) { module.exports.image = function (modules, imagePath, config) { const options = createGraphvizOptions(config); + options.type = path.extname(imagePath).replace('.', ''); + return checkGraphvizInstalled() .then(() => { return createGraph(modules, config, options) diff --git a/test/api.js b/test/api.js index ea598c3e..b3b49a5d 100644 --- a/test/api.js +++ b/test/api.js @@ -87,6 +87,15 @@ describe('Madge', () => { }); }); + it('rejects on unsupported image format', (done) => { + madge(__dirname + '/files/cjs/a.js') + .then((res) => res.image('image.zyx')) + .catch((err) => { + err.message.should.match(/Format: "zyx" not recognized/); + done(); + }); + }); + it('writes image to file', (done) => { madge(__dirname + '/files/cjs/a.js') .then((res) => res.image(imagePath)) From ed014473b1de3946cf96a53532977bdf51dc71bb Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 14:39:17 +0200 Subject: [PATCH 14/39] Enable Node.js 6 on Travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3ac3a5f3..deae08fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: node_js node_js: - 4 +- 6 before_install: - sudo apt-get update From 6088c7e78be17c4ab137b9f7e626b81932c14111 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 14:45:50 +0200 Subject: [PATCH 15/39] Enable syntax highlighting in README --- README.md | 109 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 6c0a1f46..b6b88d01 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,9 @@ Here's an example generated from the madge source using the command `madge bin/c # Installation - $ npm -g install madge +```sh +$ npm -g install madge +``` ## Graphviz (optional) @@ -41,11 +43,15 @@ Here's an example generated from the madge source using the command `madge bin/c ### Mac OS X - $ brew install graphviz || port install graphviz +```sh +$ brew install graphviz || port install graphviz +``` ### Ubuntu - $ apt-get install graphviz +```sh +$ apt-get install graphviz +``` # API @@ -61,54 +67,63 @@ Returns a `Promise` resolved with the Madge instance object. > Returns an `Object` with all dependencies. - const madge = require('madge'); +```javascript +const madge = require('madge'); - madge('path/to/app.js').then((res) => { - console.log(res.obj()); - }); +madge('path/to/app.js').then((res) => { + console.log(res.obj()); +}); +``` #### .circular() > Returns an `Array` with all modules that has circular dependencies. - const madge = require('madge'); +```javascript +const madge = require('madge'); - madge('path/to/app.js').then((res) => { - console.log(res.circular()); - }); +madge('path/to/app.js').then((res) => { + console.log(res.circular()); +}); +``` #### .depends() > Returns an `Array` with all modules that depends on a given module. - const madge = require('madge'); +```javascript +const madge = require('madge'); - madge('path/to/app.js').then((res) => { - console.log(res.depends()); - }); +madge('path/to/app.js').then((res) => { + console.log(res.depends()); +}); +``` #### .dot() > Returns a `Promise` resolved with a DOT representation of the module dependency graph. - const madge = require('madge'); +```javascript +const madge = require('madge'); - madge('path/to/app.js') - .then((res) => res.dot()) - .then((output) => { - console.log(output; - }); +madge('path/to/app.js') + .then((res) => res.dot()) + .then((output) => { + console.log(output); + }); +``` #### .image(imagePath: string) > Write the graph as an image to the given image path. The [image format](http://www.graphviz.org/content/output-formats) to use is determined from the file extension. Returns a `Promise`. - const madge = require('madge'); - - madge('path/to/app.js') - .then((res) => res.image('path/to/image.svg')) - }); +```javascript +const madge = require('madge'); +madge('path/to/app.js') + .then((res) => res.image('path/to/image.svg')) +}); +``` # Configuration @@ -129,10 +144,12 @@ Property | Type | Default | Description > Note that when running the CLI it's possible to use a runtime configuration file. The config should placed in `.madgerc` in your project or home folder. Look [here](https://github.com/dominictarr/rc#standards) for alternative locations for the file. Here's an example: - { - "showFileExtension": true, - "fontSize": "10px" - } +```json +{ + "showFileExtension": true, + "fontSize": "10px" +} +``` # CLI @@ -140,37 +157,53 @@ Property | Type | Default | Description > List all module dependencies - $ madge path/src/app.js +```sh +$ madge path/src/app.js +``` > Finding circular dependencies - $ madge --circular path/src/app.js +```sh +$ madge --circular path/src/app.js +``` > Show modules that depends on a given module - $ madge --depends 'wheels' path/src/app.js +```sh +$ madge --depends 'wheels' path/src/app.js +``` > Excluding modules - $ madge --exclude '^foo$|^bar$|^tests' path/src/app.js +```sh +$ madge --exclude '^foo$|^bar$|^tests' path/src/app.js +``` > Save graph as a PNG image (graphviz required) - $ madge --image graph.png path/src/app.js +```sh +$ madge --image graph.png path/src/app.js +``` > Save graph as a [DOT](http://en.wikipedia.org/wiki/DOT_language) file for further processing (graphviz required) - $ madge --dot path/src/app.js > graph.gv +```sh +$ madge --dot path/src/app.js > graph.gv +``` # Debugging > To enable debugging output if you encounter problems, run madge in the following way - $ DEBUG=* madge path/src/app.js +```sh +$ DEBUG=* madge path/src/app.js +``` # Running tests - $ npm test +```sh +$ npm test +``` # FAQ From 893e5792b70626409d0962e9d82597ff911e3593 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 16:13:58 +0200 Subject: [PATCH 16/39] =?UTF-8?q?Disable=20colors=20in=20debug=20output=20?= =?UTF-8?q?when=20running=20with=20=E2=80=94-no-color?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/cli.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/cli.js b/bin/cli.js index 0bdb10c6..58411d36 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,6 +1,7 @@ #!/usr/bin/env node 'use strict'; +const process = require('process'); const program = require('commander'); const rc = require('rc')('madge'); const debug = require('debug')('madge'); @@ -30,6 +31,10 @@ if (!program.args.length) { process.exit(1); } +if (!program.color) { + process.env.DEBUG_COLORS = false; +} + if (rc.config) { debug('using runtime configuration from %s', rc.config); } From e1ab485a0e6478a5ec08f0056894e38aee695c03 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 16:23:57 +0200 Subject: [PATCH 17/39] Show stracktrace on errors --- lib/graph.js | 2 +- lib/output.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/graph.js b/lib/graph.js index ab4188df..793c257a 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -99,7 +99,7 @@ function createGraph(modules, config, options) { module.exports.image = function (modules, imagePath, config) { const options = createGraphvizOptions(config); - options.type = path.extname(imagePath).replace('.', ''); + options.type = path.extname(imagePath).replace('.', '') || 'png'; return checkGraphvizInstalled() .then(() => { diff --git a/lib/output.js b/lib/output.js index 16347b0d..64575c64 100644 --- a/lib/output.js +++ b/lib/output.js @@ -118,5 +118,5 @@ module.exports.depends = function (modules, opts) { * @return {undefined} */ module.exports.error = function (err, opts) { - console.log(c(err, 'red', opts.colors)); + console.log(c(err.stack ? err.stack : err, 'red', opts.colors)); }; From 95ae708ba9e15810fc7423b888cf5f3ba28cdaa2 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 17:15:24 +0200 Subject: [PATCH 18/39] Add support for custom GraphViz path --- README.md | 1 + lib/api.js | 5 +++-- lib/graph.js | 29 ++++++++++++++++++++++------- test/api.js | 10 ++++++++++ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b6b88d01..0a997f9e 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ Property | Type | Default | Description `noDependencyColor` | String | #cfffac | The color to use for nodes with no dependencies `cyclicNodeColor` | String | #ff6c60 | The color to used for circular dependencies `edgeColor` | String | #757575 | The edge color to use in the graph +`graphVizPath` | String | null | Set GraphViz path (if not in your path) > Note that when running the CLI it's possible to use a runtime configuration file. The config should placed in `.madgerc` in your project or home folder. Look [here](https://github.com/dominictarr/rc#standards) for alternative locations for the file. Here's an example: diff --git a/lib/api.js b/lib/api.js index 30ef2109..6f70b14d 100644 --- a/lib/api.js +++ b/lib/api.js @@ -19,7 +19,8 @@ const defaultConfig = { nodeColor: '#c6c5fe', noDependencyColor: '#cfffac', cyclicNodeColor: '#ff6c60', - edgeColor: '#757575' + edgeColor: '#757575', + graphVizPath: false }; class Madge { @@ -185,7 +186,7 @@ class Madge { * @return {Promise} */ dot() { - return graph.dot(this.tree); + return graph.dot(this.tree, this.config); } /** diff --git a/lib/graph.js b/lib/graph.js index 793c257a..6bd574b5 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -18,12 +18,22 @@ function setNodeColor(node, color) { /** * Check if Graphviz is installed on the system. + * @param {Object} config * @return {Promise} */ -function checkGraphvizInstalled() { - return exec('gvpr -V').catch((error) => { - throw new Error('Graphviz could not be found. Ensure that "gvpr" is in your $PATH.\n' + error); - }); +function checkGraphvizInstalled(config) { + if (config.graphVizPath) { + const cmd = path.join(config.graphVizPath, 'gvpr -V'); + return exec(cmd) + .catch(() => { + throw new Error('Could not execute ' + cmd); + }); + } + + return exec('gvpr -V') + .catch((error) => { + throw new Error('Graphviz could not be found. Ensure that "gvpr" is in your $PATH.\n' + error); + }); } /** @@ -62,6 +72,10 @@ function createGraph(modules, config, options) { const nodes = {}; const cyclicModules = cyclic(modules).reduce((a, b) => a.concat(b), []); + if (config.graphVizPath) { + g.setGraphVizPath(config.graphVizPath); + } + Object.keys(modules).forEach((id) => { nodes[id] = nodes[id] || g.addNode(id); @@ -101,7 +115,7 @@ module.exports.image = function (modules, imagePath, config) { options.type = path.extname(imagePath).replace('.', '') || 'png'; - return checkGraphvizInstalled() + return checkGraphvizInstalled(config) .then(() => { return createGraph(modules, config, options) .then((image) => fs.writeFile(imagePath, image)); @@ -111,13 +125,14 @@ module.exports.image = function (modules, imagePath, config) { /** * Return the module dependency graph as DOT output. * @param {Object} modules + * @param {Object} config * @return {Promise} */ -module.exports.dot = function (modules) { +module.exports.dot = function (modules, config) { const nodes = {}; const g = graphviz.digraph('G'); - return checkGraphvizInstalled() + return checkGraphvizInstalled(config) .then(() => { Object.keys(modules).forEach((id) => { nodes[id] = nodes[id] || g.addNode(id); diff --git a/test/api.js b/test/api.js index b3b49a5d..272ec8a8 100644 --- a/test/api.js +++ b/test/api.js @@ -96,6 +96,16 @@ describe('Madge', () => { }); }); + it('rejects if graphviz is not installed', (done) => { + madge(__dirname + '/files/cjs/a.js', {graphVizPath: '/invalid/path'}) + .then((res) => res.image('image.png')) + .catch((err) => { + console.log(err.message); + err.message.should.eql('Could not execute /invalid/path/gvpr -V'); + done(); + }); + }); + it('writes image to file', (done) => { madge(__dirname + '/files/cjs/a.js') .then((res) => res.image(imagePath)) From ab3c2088d9547ab2ce922b84ba9a0375a94789b9 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 18:11:22 +0200 Subject: [PATCH 19/39] Update test for circular CommonJS deps --- test/cjs.js | 2 +- test/files/cjs/circular/a.js | 4 +++- test/files/cjs/circular/c.js | 1 - test/files/cjs/circular/d.js | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 test/files/cjs/circular/d.js diff --git a/test/cjs.js b/test/cjs.js index ef8871cc..d280f132 100644 --- a/test/cjs.js +++ b/test/cjs.js @@ -32,7 +32,7 @@ describe('CommonJS', () => { it('finds circular dependencies', (done) => { madge(dir + '/circular/a.js').then((res) => { res.circular().should.eql([ - ['a', 'b', 'c'] + ['a', 'd'] ]); done(); }).catch(done); diff --git a/test/files/cjs/circular/a.js b/test/files/cjs/circular/a.js index f8a21a13..84c3d5e4 100644 --- a/test/files/cjs/circular/a.js +++ b/test/files/cjs/circular/a.js @@ -1 +1,3 @@ -var b = require('./b'); \ No newline at end of file +var b = require('./b'); +var c = require('./c'); +var d = require('./d'); \ No newline at end of file diff --git a/test/files/cjs/circular/c.js b/test/files/cjs/circular/c.js index 63443cd1..e69de29b 100644 --- a/test/files/cjs/circular/c.js +++ b/test/files/cjs/circular/c.js @@ -1 +0,0 @@ -var a = require('./a'); \ No newline at end of file diff --git a/test/files/cjs/circular/d.js b/test/files/cjs/circular/d.js new file mode 100644 index 00000000..63443cd1 --- /dev/null +++ b/test/files/cjs/circular/d.js @@ -0,0 +1 @@ +var a = require('./a'); \ No newline at end of file From e3c2c9e6c6dc99506e19e511ca9843280d1b453e Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 9 Aug 2016 18:24:13 +0200 Subject: [PATCH 20/39] Update examples to use SVG --- .npmignore | 3 +-- README.md | 23 ++++++++++++++--------- bin/cli.js | 2 +- examples/madge.png | Bin 33034 -> 0 bytes examples/small.png | Bin 12680 -> 0 bytes package.json | 5 ++++- 6 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 examples/madge.png delete mode 100644 examples/small.png diff --git a/.npmignore b/.npmignore index 0c30401e..30d74d25 100644 --- a/.npmignore +++ b/.npmignore @@ -1,2 +1 @@ -test -examples \ No newline at end of file +test \ No newline at end of file diff --git a/README.md b/README.md index 0a997f9e..97ba18c7 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,22 @@ Works for JS (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus); See [CHANGELOG](CHANGELOG.md) for latest changes. ## Examples -Here's a very simple example of a generated image. -![](examples/small.png) +> Simple example of a generated image. - - blue = has dependencies - - green = has no dependencies - - red = has circular dependencies + + + -Here's an example generated from the madge source using the command `madge bin/cli.js --directory . --image examples/madge.png`. +* blue = has dependencies +* green = has no dependencies +* red = has circular dependencies -![](examples/madge.png) +> Example generated from the madge source. + + + + # Installation @@ -180,10 +185,10 @@ $ madge --depends 'wheels' path/src/app.js $ madge --exclude '^foo$|^bar$|^tests' path/src/app.js ``` -> Save graph as a PNG image (graphviz required) +> Save graph as a SVG image (graphviz required) ```sh -$ madge --image graph.png path/src/app.js +$ madge --image graph.svg path/src/app.js ``` > Save graph as a [DOT](http://en.wikipedia.org/wiki/DOT_language) file for further processing (graphviz required) diff --git a/bin/cli.js b/bin/cli.js index 58411d36..ee83a761 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -18,7 +18,7 @@ program .option('--circular', 'show circular dependencies') .option('--depends ', 'show modules that depends on the given id') .option('--json', 'show output as JSON') - .option('--image ', 'write graph to file as a PNG image') + .option('--image ', 'write graph to file as an image') .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') .option('--no-color', 'disable color in output and image', false) diff --git a/examples/madge.png b/examples/madge.png deleted file mode 100644 index f662a48bb774a1a7aa9b9b413f9e92aa0e65363a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33034 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

kU^ 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%0DK%BRxI6J$)V*d|4O%tvu5rt^(5x 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 na?T^xde6iC|6k) Date: Tue, 9 Aug 2016 21:55:31 +0200 Subject: [PATCH 21/39] Show debug output on Travis --- .travis.yml | 6 +++++- package.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index deae08fd..89b96b03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,4 +6,8 @@ node_js: before_install: - sudo apt-get update -- sudo apt-get install graphviz \ No newline at end of file +- sudo apt-get install graphviz + +script: +- npm test +- npm run debug \ No newline at end of file diff --git a/package.json b/package.json index 846c4088..223ea43d 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "mocha": "mocha test/*.js", "watch": "mocha --watch --growl test/*.js", "lint": "eslint bin/cli.js lib test/*.js", - "madge": "bin/cli.js bin/cli.js", + "debug": "DEBUG=* bin/cli.js bin/cli.js", "generate": "npm run generate:small && npm run generate:madge", "generate:small": "bin/cli.js test/files/cjs/circular/a.js --image simple.svg", "generate:madge": "bin/cli.js bin/cli.js --directory . --image madge.svg", From 1747c0836c6d27d98f52ad18e32ace19e25f1a70 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 10 Aug 2016 09:10:51 +0200 Subject: [PATCH 22/39] =?UTF-8?q?Add=20documentation=20for=20=E2=80=94-bas?= =?UTF-8?q?edir=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +++++------- bin/cli.js | 8 ++++++-- lib/api.js | 11 ++++++++--- package.json | 2 +- test/api.js | 1 - 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 97ba18c7..641a6860 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,11 @@ [![NPM Status](http://img.shields.io/npm/dm/madge.svg?style=flat-square)](https://www.npmjs.org/package/madge) [![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](https://paypal.me/pahen) -A tool for generating a visual graph from your module dependencies. Can also find circular dependencies and give you other useful info about your dependencies. The dependencies are generated using Joel Kemp's awesome [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree). +**Madge** is a developer tool for generating a visual graph of your module dependencies. Can also find circular dependencies and give you other useful info. The dependencies are generated using Joel Kemp's awesome [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree). -Works for JS (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus); basically, any filetype supported by [precinct](https://github.com/mrjoelkemp/node-precinct). - - - For CommonJS modules, 3rd party dependencies (npm installed dependencies) are exluded in the tree - - Dependency path resolutions are handled by [filing-cabinet](https://github.com/mrjoelkemp/node-filing-cabinet) - - Supports RequireJS and Webpack loaders - - All core Node modules (assert, path, fs, etc) are removed from the dependency list by default +* Works for JavaScript (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus) +* For CommonJS modules, NPM installed dependencies are exluded by default but can be enabled in config +* All core Node.js modules (assert, path, fs, etc) are excluded See [CHANGELOG](CHANGELOG.md) for latest changes. @@ -134,6 +131,7 @@ madge('path/to/app.js') Property | Type | Default | Description --- | --- | --- | --- +`baseDir` | String | null | Base directory to use when resolving paths (defaults to `filePath` directory) `includeNpm` | Boolean | false | If node_modules should be included `showFileExtension` | Boolean | false | If file extensions should be included in module name `requireConfig` | String | null | RequireJS config for resolving aliased modules diff --git a/bin/cli.js b/bin/cli.js index ee83a761..090e0f1f 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -12,7 +12,7 @@ const madge = require('../lib/api'); program .version(version) .usage('[options] ') - .option('--directory ', '') + .option('--basedir ', 'base directory to use when resolving paths') .option('--list', 'show list of all dependencies (default)') .option('--summary', 'show summary of all dependencies') .option('--circular', 'show circular dependencies') @@ -45,12 +45,16 @@ delete config._; delete config.config; delete config.configs; -['directory', 'layout', 'requireConfig', 'webpackConfig'].forEach((option) => { +['layout', 'requireConfig', 'webpackConfig'].forEach((option) => { if (program[option]) { config[option] = program[option]; } }); +if (program.basedir) { + config.baseDir = program.basedir; +} + if (!program.color) { config.backgroundColor = '#ffffff'; config.nodeColor = '#00000'; diff --git a/lib/api.js b/lib/api.js index 6f70b14d..5b1086d3 100644 --- a/lib/api.js +++ b/lib/api.js @@ -8,6 +8,7 @@ const cyclic = require('./cyclic'); const graph = require('./graph'); const defaultConfig = { + baseDir: null, showFileExtension: false, includeNpm: false, requireConfig: null, @@ -37,11 +38,15 @@ class Madge { } this.config = Object.assign({}, defaultConfig, config); - debug('using config', this.config); + debug('using config %o', this.config); + + this.filePath = path.resolve(filePath); + debug('using file path: %s', this.filePath); - this.filePath = filePath; this.excludeRegex = this.config.exclude ? new RegExp(this.config.exclude) : false; - this.baseDir = this.config.directory ? path.resolve(this.config.directory) : path.dirname(filePath); + + this.baseDir = path.resolve(this.config.baseDir || path.dirname(filePath)); + debug('using base directory: %s', this.baseDir); } /** diff --git a/package.json b/package.json index 223ea43d..cc8c0772 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "debug": "DEBUG=* bin/cli.js bin/cli.js", "generate": "npm run generate:small && npm run generate:madge", "generate:small": "bin/cli.js test/files/cjs/circular/a.js --image simple.svg", - "generate:madge": "bin/cli.js bin/cli.js --directory . --image madge.svg", + "generate:madge": "bin/cli.js bin/cli.js --basedir . --image madge.svg", "release": "npm test && release-it -n -i patch", "release:minor": "npm run test && release-it -n -i minor", "release:major": "npm run test && release-it -n -i major" diff --git a/test/api.js b/test/api.js index 272ec8a8..66f19fa0 100644 --- a/test/api.js +++ b/test/api.js @@ -100,7 +100,6 @@ describe('Madge', () => { madge(__dirname + '/files/cjs/a.js', {graphVizPath: '/invalid/path'}) .then((res) => res.image('image.png')) .catch((err) => { - console.log(err.message); err.message.should.eql('Could not execute /invalid/path/gvpr -V'); done(); }); From 1d9f82004769e1f98acc95ac405816ae36a10b50 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 10 Aug 2016 09:14:02 +0200 Subject: [PATCH 23/39] Only support one output type in CLI --- bin/cli.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 090e0f1f..43571697 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -65,15 +65,15 @@ if (!program.color) { madge(program.args[0], config) .then((res) => { - if (program.list || (!program.summary && !program.circular && !program.depends && !program.image && !program.dot)) { - output.list(res.obj(), { + if (program.summary) { + return output.summary(res.obj(), { colors: program.color, json: program.json }); } - if (program.summary) { - output.summary(res.obj(), { + if (program.depends) { + return output.depends(res.depends(program.depends), { colors: program.color, json: program.json }); @@ -90,13 +90,8 @@ madge(program.args[0], config) if (circular.length) { process.exit(1); } - } - if (program.depends) { - output.depends(res.depends(program.depends), { - colors: program.color, - json: program.json - }); + return; } if (program.image) { @@ -108,6 +103,11 @@ madge(program.args[0], config) process.stdout.write(output); }); } + + return output.list(res.obj(), { + colors: program.color, + json: program.json + }); }) .catch((err) => { output.error(err, { From 0cc89b12406ee6100b69b8992f0d56f1b6817ac0 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 10 Aug 2016 09:22:10 +0200 Subject: [PATCH 24/39] Show message when image was created successfully --- README.md | 2 +- bin/cli.js | 4 +++- lib/graph.js | 3 ++- test/api.js | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 641a6860..e8f615ae 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ madge('path/to/app.js') #### .image(imagePath: string) -> Write the graph as an image to the given image path. The [image format](http://www.graphviz.org/content/output-formats) to use is determined from the file extension. Returns a `Promise`. +> Write the graph as an image to the given image path. The [image format](http://www.graphviz.org/content/output-formats) to use is determined from the file extension. Returns a `Promise` resolved with a full path to the written image. ```javascript const madge = require('madge'); diff --git a/bin/cli.js b/bin/cli.js index 43571697..df5b117b 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -95,7 +95,9 @@ madge(program.args[0], config) } if (program.image) { - return res.image(program.image); + return res.image(program.image).then((imagePath) => { + console.log('Image created at %s', imagePath); + }); } if (program.dot) { diff --git a/lib/graph.js b/lib/graph.js index 6bd574b5..3fca9912 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -118,7 +118,8 @@ module.exports.image = function (modules, imagePath, config) { return checkGraphvizInstalled(config) .then(() => { return createGraph(modules, config, options) - .then((image) => fs.writeFile(imagePath, image)); + .then((image) => fs.writeFile(imagePath, image)) + .then(() => path.resolve(imagePath)); }); }; diff --git a/test/api.js b/test/api.js index 66f19fa0..94c83db7 100644 --- a/test/api.js +++ b/test/api.js @@ -108,7 +108,9 @@ describe('Madge', () => { it('writes image to file', (done) => { madge(__dirname + '/files/cjs/a.js') .then((res) => res.image(imagePath)) - .then(() => { + .then((writtenImagePath) => { + writtenImagePath.should.eql(imagePath); + return fs .exists(imagePath) .then((exists) => { From 4a3fa29ea1a80616acdc5b7a925b6b4f3fa747f5 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 10 Aug 2016 09:26:17 +0200 Subject: [PATCH 25/39] Fix typo in comment --- lib/api.js | 2 +- lib/output.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/api.js b/lib/api.js index 5b1086d3..185c577b 100644 --- a/lib/api.js +++ b/lib/api.js @@ -177,7 +177,7 @@ class Madge { * Return a list of modules that depends on the given module. * @api public * @param {String} id - * @return {Array|Object} + * @return {Array} */ depends(id) { return Object diff --git a/lib/output.js b/lib/output.js index 64575c64..142f98af 100644 --- a/lib/output.js +++ b/lib/output.js @@ -97,16 +97,16 @@ module.exports.circular = function (circular, opts) { /** * Print the result from Madge.depends(). - * @param {Object} modules + * @param {Array} depends * @param {Object} opts * @return {undefined} */ -module.exports.depends = function (modules, opts) { +module.exports.depends = function (depends, opts) { if (opts.json) { - return printJSON(modules); + return printJSON(depends); } - modules.forEach((id) => { + depends.forEach((id) => { console.log(c(id, 'grey', opts.colors)); }); }; From 6e74c33dbff42d849d05604f559be752673379b9 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 10 Aug 2016 09:56:31 +0200 Subject: [PATCH 26/39] Share same debug instance when logging --- bin/cli.js | 4 ++-- lib/api.js | 8 ++++---- lib/log.js | 3 +++ 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 lib/log.js diff --git a/bin/cli.js b/bin/cli.js index df5b117b..1b123aad 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -4,7 +4,7 @@ const process = require('process'); const program = require('commander'); const rc = require('rc')('madge'); -const debug = require('debug')('madge'); +const log = require('../lib/log'); const version = require('../package.json').version; const output = require('../lib/output'); const madge = require('../lib/api'); @@ -36,7 +36,7 @@ if (!program.color) { } if (rc.config) { - debug('using runtime configuration from %s', rc.config); + log('using runtime configuration from %s', rc.config); } const config = Object.assign({}, rc); diff --git a/lib/api.js b/lib/api.js index 185c577b..3e0a14e1 100644 --- a/lib/api.js +++ b/lib/api.js @@ -2,7 +2,7 @@ const path = require('path'); const fs = require('mz/fs'); -const debug = require('debug')('madge'); +const log = require('./log'); const dependencyTree = require('dependency-tree'); const cyclic = require('./cyclic'); const graph = require('./graph'); @@ -38,15 +38,15 @@ class Madge { } this.config = Object.assign({}, defaultConfig, config); - debug('using config %o', this.config); + log('using config %o', this.config); this.filePath = path.resolve(filePath); - debug('using file path: %s', this.filePath); + log('using file path: %s', this.filePath); this.excludeRegex = this.config.exclude ? new RegExp(this.config.exclude) : false; this.baseDir = path.resolve(this.config.baseDir || path.dirname(filePath)); - debug('using base directory: %s', this.baseDir); + log('using base directory: %s', this.baseDir); } /** diff --git a/lib/log.js b/lib/log.js new file mode 100644 index 00000000..3e3a0fb0 --- /dev/null +++ b/lib/log.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('debug')('madge'); From a3624348698a77301c1842a5667b768ae64e28d4 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 10 Aug 2016 10:53:50 +0200 Subject: [PATCH 27/39] Support excluding modules using RegExp --- README.md | 7 ++--- bin/cli.js | 5 ++++ lib/api.js | 76 +++++++++++++++++++++++++++++++++-------------------- test/api.js | 12 +++++++++ test/es6.js | 11 -------- 5 files changed, 68 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index e8f615ae..b0c796dc 100644 --- a/README.md +++ b/README.md @@ -134,17 +134,18 @@ Property | Type | Default | Description `baseDir` | String | null | Base directory to use when resolving paths (defaults to `filePath` directory) `includeNpm` | Boolean | false | If node_modules should be included `showFileExtension` | Boolean | false | If file extensions should be included in module name +`excludeRegExp` | Array | false | An array of RegExp to use for excluding modules from the graph `requireConfig` | String | null | RequireJS config for resolving aliased modules `webpackConfig` | String | null | Webpack config for resolving aliased modules `layout` | String | dot | Layout to use in graph `fontName` | String | Arial | Font name to use in graph -`fontSize` | String | 14px | Font size to use in graph +`fontSize` | String | 14px | Font size to use in graph `backgroundColor` | String | #000000 | Background color for the graph `nodeColor` | String | #c6c5fe | The default node color to use in the graph `noDependencyColor` | String | #cfffac | The color to use for nodes with no dependencies `cyclicNodeColor` | String | #ff6c60 | The color to used for circular dependencies `edgeColor` | String | #757575 | The edge color to use in the graph -`graphVizPath` | String | null | Set GraphViz path (if not in your path) +`graphVizPath` | String | null | Custom GraphViz path > Note that when running the CLI it's possible to use a runtime configuration file. The config should placed in `.madgerc` in your project or home folder. Look [here](https://github.com/dominictarr/rc#standards) for alternative locations for the file. Here's an example: @@ -180,7 +181,7 @@ $ madge --depends 'wheels' path/src/app.js > Excluding modules ```sh -$ madge --exclude '^foo$|^bar$|^tests' path/src/app.js +$ madge --exclude-regexp '^(foo|bar)$' path/src/app.js ``` > Save graph as a SVG image (graphviz required) diff --git a/bin/cli.js b/bin/cli.js index 1b123aad..dc88946f 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -18,6 +18,7 @@ program .option('--circular', 'show circular dependencies') .option('--depends ', 'show modules that depends on the given id') .option('--json', 'show output as JSON') + .option('--exclude-regexp ', 'Exclude modules using a RegExp') .option('--image ', 'write graph to file as an image') .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') @@ -55,6 +56,10 @@ if (program.basedir) { config.baseDir = program.basedir; } +if (program.excludeRegexp) { + config.excludeRegExp = [program.excludeRegexp]; +} + if (!program.color) { config.backgroundColor = '#ffffff'; config.nodeColor = '#00000'; diff --git a/lib/api.js b/lib/api.js index 3e0a14e1..da045fb8 100644 --- a/lib/api.js +++ b/lib/api.js @@ -9,6 +9,7 @@ const graph = require('./graph'); const defaultConfig = { baseDir: null, + excludeRegExp: false, showFileExtension: false, includeNpm: false, requireConfig: null, @@ -24,6 +25,43 @@ const defaultConfig = { graphVizPath: false }; +/** + * Exclude modules from tree using RegExp. + * @param {Object} tree + * @param {Array} excludeRegExp + * @return {Object} + */ +function excludeFromTreeByRegExp(tree, excludeRegExp) { + const regExpList = excludeRegExp.map((re) => new RegExp(re)); + + function regExpFilter(id) { + return regExpList.findIndex((regexp) => regexp.test(id)) < 0; + } + + return Object + .keys(tree) + .filter(regExpFilter) + .reduce((acc, id) => { + acc[id] = tree[id].filter(regExpFilter); + return acc; + }, {}); +} + +/** + * Sort given tree. + * @param {Object} tree + * @return {Object} + */ +function sortDependencies(tree) { + return Object + .keys(tree) + .sort() + .reduce((acc, id) => { + acc[id] = tree[id].sort(); + return acc; + }, {}); +} + class Madge { /** * Class constructor. @@ -43,8 +81,6 @@ class Madge { this.filePath = path.resolve(filePath); log('using file path: %s', this.filePath); - this.excludeRegex = this.config.exclude ? new RegExp(this.config.exclude) : false; - this.baseDir = path.resolve(this.config.baseDir || path.dirname(filePath)); log('using base directory: %s', this.baseDir); } @@ -77,21 +113,16 @@ class Madge { filter: this.pathFilter.bind(this) })); - this.sortDependencies(); + if (this.config.excludeRegExp) { + this.tree = excludeFromTreeByRegExp(this.tree, this.config.excludeRegExp); + } + + this.tree = sortDependencies(this.tree); return this; }); } - /** - * Function used to determine if a module (and its subtree) should be included in the dependency tree. - * @param {String} path - * @return {Boolean} - */ - pathFilter(path) { - return (this.config.includeNpm || path.indexOf('node_modules') < 0) && !this.isExcluded(path); - } - /** * Convert deep tree produced by `dependency-tree` to internal format used by Madge. * @param {Object} tree @@ -134,25 +165,12 @@ class Madge { } /** - * Sort dependencies by name. - */ - sortDependencies() { - this.tree = Object - .keys(this.tree) - .sort() - .reduce((acc, id) => { - acc[id] = this.tree[id].sort(); - return acc; - }, {}); - } - - /** - * Check if module should be excluded. - * @param {String} id + * Function used to determine if a module (and its subtree) should be included in the dependency tree. + * @param {String} path * @return {Boolean} */ - isExcluded(id) { - return this.excludeRegex && id.match(this.excludeRegex); + pathFilter(path) { + return this.config.includeNpm || path.indexOf('node_modules') < 0; } /** diff --git a/test/api.js b/test/api.js index 94c83db7..1e9744ef 100644 --- a/test/api.js +++ b/test/api.js @@ -33,6 +33,18 @@ describe('Madge', () => { }).catch(done); }); + it('can exclude modules using RegExp', (done) => { + madge(__dirname + '/files/cjs/a.js', { + excludeRegExp: ['^b$'] + }).then((res) => { + res.obj().should.eql({ + a: ['c'], + c: [] + }); + done(); + }).catch(done); + }); + describe('#obj', () => { it('returns dependency object', (done) => { madge(__dirname + '/files/cjs/a.js').then((res) => { diff --git a/test/es6.js b/test/es6.js index a56c57f6..1892ffc0 100644 --- a/test/es6.js +++ b/test/es6.js @@ -81,15 +81,4 @@ describe('ES6', () => { done(); }).catch(done); }); - - it('can exclude modules', (done) => { - madge(dir + '/normal/a.js', { - exclude: '.*\/sub' - }).then((res) => { - res.obj().should.eql({ - 'a': [] - }); - done(); - }).catch(done); - }); }); From 42ccbb8c23bbc3f89e91ff75eaadbeedaea441d6 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Fri, 12 Aug 2016 21:16:28 +0200 Subject: [PATCH 28/39] =?UTF-8?q?Remove=20release-it=20in=20favor=20of=20?= =?UTF-8?q?=E2=80=9Cnpm=20version=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index cc8c0772..fd108a6a 100644 --- a/package.json +++ b/package.json @@ -34,10 +34,7 @@ "debug": "DEBUG=* bin/cli.js bin/cli.js", "generate": "npm run generate:small && npm run generate:madge", "generate:small": "bin/cli.js test/files/cjs/circular/a.js --image simple.svg", - "generate:madge": "bin/cli.js bin/cli.js --basedir . --image madge.svg", - "release": "npm test && release-it -n -i patch", - "release:minor": "npm run test && release-it -n -i minor", - "release:major": "npm run test && release-it -n -i major" + "generate:madge": "bin/cli.js bin/cli.js --basedir . --image madge.svg" }, "dependencies": { "colors": "^1.1.2", @@ -52,7 +49,6 @@ "@aptoma/eslint-config": "^4.0.0", "eslint": "^3.0.0", "mocha": "^2.3.3", - "release-it": "^2.4.0", "should": "^9.0.2" } } From 127697d276c1310b540efea71ef4b7548ee7423d Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Fri, 12 Aug 2016 21:27:08 +0200 Subject: [PATCH 29/39] Use module chalk instead of colors --- bin/cli.js | 8 +------- lib/output.js | 34 ++++++++++++++-------------------- package.json | 2 +- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index dc88946f..d7fd6644 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -72,14 +72,12 @@ madge(program.args[0], config) .then((res) => { if (program.summary) { return output.summary(res.obj(), { - colors: program.color, json: program.json }); } if (program.depends) { return output.depends(res.depends(program.depends), { - colors: program.color, json: program.json }); } @@ -88,7 +86,6 @@ madge(program.args[0], config) const circular = res.circular(); output.circular(circular, { - colors: program.color, json: program.json }); @@ -112,14 +109,11 @@ madge(program.args[0], config) } return output.list(res.obj(), { - colors: program.color, json: program.json }); }) .catch((err) => { - output.error(err, { - colors: program.color - }); + output.error(err); process.exit(1); }); diff --git a/lib/output.js b/lib/output.js index 142f98af..81d2a514 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1,17 +1,11 @@ 'use strict'; -require('colors'); +const chalk = require('chalk'); -/** - * Return colored string. - * @param {String} str - * @param {String} name - * @param {Boolean} use - * @return {String} - */ -function c(str, name, use) { - return use ? String(str)[name] : str; -} +const red = chalk.red; +const cyan = chalk.cyan; +const grey = chalk.grey; +const green = chalk.green; /** * Print given object as JSON. @@ -36,9 +30,9 @@ module.exports.list = function (modules, opts) { } Object.keys(modules).forEach((id) => { - console.log(c(id, 'cyan', opts.colors)); + console.log(cyan(id)); modules[id].forEach((depId) => { - console.log(c(' ' + depId, 'grey', opts.colors)); + console.log(grey(' ' + depId)); }); }); }; @@ -60,7 +54,7 @@ module.exports.summary = function (modules, opts) { if (opts.json) { o[id] = modules[id].length; } else { - console.log(c(id + ': ', 'grey', opts.colors) + c(modules[id].length, 'cyan', opts.colors)); + console.log(grey(id + ': ') + cyan(modules[id].length)); } }); @@ -81,14 +75,14 @@ module.exports.circular = function (circular, opts) { } if (!circular.length) { - console.log(c('No circular dependencies found!', 'green', opts.colors)); + console.log(green('No circular dependencies found!')); } else { circular.forEach((path, idx) => { path.forEach((module, idx) => { if (idx) { - process.stdout.write(c(' -> ', 'cyan', opts.colors)); + process.stdout.write(cyan(' -> ')); } - process.stdout.write(c(module, 'red', opts.colors)); + process.stdout.write(red(module)); }); process.stdout.write('\n'); }); @@ -107,7 +101,7 @@ module.exports.depends = function (depends, opts) { } depends.forEach((id) => { - console.log(c(id, 'grey', opts.colors)); + console.log(grey(id)); }); }; @@ -117,6 +111,6 @@ module.exports.depends = function (depends, opts) { * @param {Object} opts * @return {undefined} */ -module.exports.error = function (err, opts) { - console.log(c(err.stack ? err.stack : err, 'red', opts.colors)); +module.exports.error = function (err) { + console.log(red(err.stack ? err.stack : err)); }; diff --git a/package.json b/package.json index fd108a6a..e9c38797 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "generate:madge": "bin/cli.js bin/cli.js --basedir . --image madge.svg" }, "dependencies": { - "colors": "^1.1.2", + "chalk": "^1.1.3", "commander": "^2.9.0", "debug": "^2.2.0", "dependency-tree": "^5.4.1", From 5880f602e3702e49a8c57127b28e14c4241712b7 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Sun, 14 Aug 2016 17:15:13 +0200 Subject: [PATCH 30/39] Add 1.0 to CHANGELOG --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ README.md | 6 +++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8184773..b8ac7675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,45 @@ # CHANGELOG +## v1.0.0 (????) + +After 4 years of adding features/fixes it started to be hard to maintain the project and fix some outstanding issues due to how madge was designed to work. + +So I decided it was high time for version 1.0 to be released and take the opportunity to do a major rewrite and reduce the size and responsibility of the project and delegate some work to [external libraries](https://github.com/mrjoelkemp/node-dependency-tree). This introduced many breaking changes. Here's the most important ones. + +**Added:** + +* Automatic module type detection thanks to [precinct](https://github.com/mrjoelkemp/node-precinct) +* Determine image format based on file extension (SVG support) +* Reading [config](README.md#configuration) from `.madgerc` (replaces `--config`) +* Option `--webpack-config` for supporting aliased module paths + +**Changed:** + +* Renamed many of the settings in the [config](README.md#configuration) +* Option `--json` should now be used instead of `--output json` +* Option `--exclude` renamed to `--exclude-regexp` +* The generation of the dependency tree is now delegated to the external module [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree) +* A single file will now be used as an entry instead if scanning entire folder(s) +* Dependencies will now be extracted recursively from the single file +* NPM installed dependencies are now excluded by default +* Node.js core modules are now excluded +* The [API](README.md#api) is now using promises + +**Removed:** + +* Option `--format` since the format is now detected automatically from the file content +* Option `--optimized` and `--main-require-module` since we no longer support RequireJS builds (r.js) +* Option `--read` +* Option `--find-nested-dependencies` +* Option `--paths` +* Option `--extensions` +* Option `--config` +* Option `--output` +* Option `--break-on-error` +* CoffeScript support +* Event callbacks `onParseFile` and `onAddModule` +* NPM shrinkwrap no longer used + ## v0.6.0 (July 06, 2016) * Refactored Madge to use ES6 and now requires Node.js 4 to run. diff --git a/README.md b/README.md index b0c796dc..4169b71b 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ [![NPM Status](http://img.shields.io/npm/dm/madge.svg?style=flat-square)](https://www.npmjs.org/package/madge) [![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](https://paypal.me/pahen) -**Madge** is a developer tool for generating a visual graph of your module dependencies. Can also find circular dependencies and give you other useful info. The dependencies are generated using Joel Kemp's awesome [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree). +**Madge** is a developer tool for generating a visual graph of your module dependencies, finding circular dependencies, and give you other useful info. Joel Kemp's awesome [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree) is used for extracting the dependency tree. * Works for JavaScript (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus) -* For CommonJS modules, NPM installed dependencies are exluded by default but can be enabled in config +* NPM installed dependencies are excluded by default (can be enabled in config) * All core Node.js modules (assert, path, fs, etc) are excluded -See [CHANGELOG](CHANGELOG.md) for latest changes. +Read the [changelog](CHANGELOG.md) for latest changes. ## Examples From 8f3bf891eceeecfd2014928490311fa78c077b69 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Sun, 14 Aug 2016 20:52:22 +0200 Subject: [PATCH 31/39] =?UTF-8?q?Add=20=E2=80=94-debug=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 +++- README.md | 5 +++-- bin/cli.js | 62 ++++++++++++++++++++++++++++++++++++++++------------ package.json | 7 +++--- 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8ac7675..2ee4fd87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ So I decided it was high time for version 1.0 to be released and take the opport * Determine image format based on file extension (SVG support) * Reading [config](README.md#configuration) from `.madgerc` (replaces `--config`) * Option `--webpack-config` for supporting aliased module paths +* Option `--debug` for turning on debug output +* Get default file to read from package.json **Changed:** @@ -19,7 +21,7 @@ So I decided it was high time for version 1.0 to be released and take the opport * Option `--json` should now be used instead of `--output json` * Option `--exclude` renamed to `--exclude-regexp` * The generation of the dependency tree is now delegated to the external module [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree) -* A single file will now be used as an entry instead if scanning entire folder(s) +* A single file will now be used as an entry instead of scanning entire folder(s) * Dependencies will now be extracted recursively from the single file * NPM installed dependencies are now excluded by default * Node.js core modules are now excluded diff --git a/README.md b/README.md index 4169b71b..aa031d79 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ * Works for JavaScript (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus) * NPM installed dependencies are excluded by default (can be enabled in config) * All core Node.js modules (assert, path, fs, etc) are excluded +* Get default file to scan from package.json (bin or main) Read the [changelog](CHANGELOG.md) for latest changes. @@ -198,10 +199,10 @@ $ madge --dot path/src/app.js > graph.gv # Debugging -> To enable debugging output if you encounter problems, run madge in the following way +> To enable debugging output if you encounter problems, run madge with the `--debug` option then throw the result in a gist when creating issues on GitHub. ```sh -$ DEBUG=* madge path/src/app.js +$ madge --debug path/src/app.js ``` # Running tests diff --git a/bin/cli.js b/bin/cli.js index d7fd6644..f26d69dc 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -2,50 +2,54 @@ 'use strict'; const process = require('process'); +const path = require('path'); +const fs = require('mz/fs'); const program = require('commander'); const rc = require('rc')('madge'); -const log = require('../lib/log'); +const readPackage = require('read-package-json'); const version = require('../package.json').version; -const output = require('../lib/output'); -const madge = require('../lib/api'); program .version(version) - .usage('[options] ') + .usage('[options] [file|dir]') .option('--basedir ', 'base directory to use when resolving paths') .option('--list', 'show list of all dependencies (default)') .option('--summary', 'show summary of all dependencies') .option('--circular', 'show circular dependencies') .option('--depends ', 'show modules that depends on the given id') .option('--json', 'show output as JSON') - .option('--exclude-regexp ', 'Exclude modules using a RegExp') + .option('--exclude-regexp ', 'exclude modules using a RegExp') .option('--image ', 'write graph to file as an image') .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') - .option('--no-color', 'disable color in output and image', false) .option('--require-config ', 'path to RequireJS config') .option('--webpack-config ', 'path to webpack config') + .option('--no-color', 'disable color in output and image', false) + .option('--debug', 'turn on debug output', false) .parse(process.argv); -if (!program.args.length) { - console.log(program.helpInformation()); - process.exit(1); +if (program.debug) { + process.env.DEBUG = '*'; } if (!program.color) { process.env.DEBUG_COLORS = false; } -if (rc.config) { - log('using runtime configuration from %s', rc.config); -} - +const log = require('../lib/log'); +const output = require('../lib/output'); +const madge = require('../lib/api'); +const target = program.args[0] || process.cwd(); const config = Object.assign({}, rc); delete config._; delete config.config; delete config.configs; +if (rc.config) { + log('using runtime configuration from %s', rc.config); +} + ['layout', 'requireConfig', 'webpackConfig'].forEach((option) => { if (program[option]) { config[option] = program[option]; @@ -68,7 +72,37 @@ if (!program.color) { config.edgeColor = '#757575'; } -madge(program.args[0], config) +fs + .stat(target) + .then((stats) => { + if (stats.isFile()) { + return program.args[0]; + } + + const pkgPath = path.join(target, 'package.json'); + + return new Promise((resolve, reject) => { + readPackage(pkgPath, (err, pkg) => { + if (err) { + reject('Could not read ' + pkgPath + '. Choose another directory or specify file.'); + return; + } + + config.baseDir = target; + + if (pkg.bin) { + log('extracted file path from "bin" entry in ' + pkgPath); + resolve(path.join(target, pkg.bin[Object.keys(pkg.bin)[0]])); + } else if (pkg.main) { + log('extracted file path from "main" entry in ' + pkgPath); + resolve(path.join(target, pkg.main)); + } else { + reject('Could not get file to scan by reading package.json. Update package.json or specify a file.'); + } + }); + }); + }) + .then((filePath) => madge(filePath, config)) .then((res) => { if (program.summary) { return output.summary(res.obj(), { diff --git a/package.json b/package.json index e9c38797..24273497 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,10 @@ "mocha": "mocha test/*.js", "watch": "mocha --watch --growl test/*.js", "lint": "eslint bin/cli.js lib test/*.js", - "debug": "DEBUG=* bin/cli.js bin/cli.js", + "debug": "bin/cli.js --debug", "generate": "npm run generate:small && npm run generate:madge", "generate:small": "bin/cli.js test/files/cjs/circular/a.js --image simple.svg", - "generate:madge": "bin/cli.js bin/cli.js --basedir . --image madge.svg" + "generate:madge": "bin/cli.js --image madge.svg" }, "dependencies": { "chalk": "^1.1.3", @@ -43,7 +43,8 @@ "dependency-tree": "^5.4.1", "graphviz": "^0.0.8", "mz": "^2.4.0", - "rc": "^1.1.6" + "rc": "^1.1.6", + "read-package-json": "^2.0.4" }, "devDependencies": { "@aptoma/eslint-config": "^4.0.0", From fd2d74f9cde79a544a2fb007f3c7758ee69ab686 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Mon, 15 Aug 2016 04:15:08 +0200 Subject: [PATCH 32/39] =?UTF-8?q?Rename=20=E2=80=94-exclude-regexp=20back?= =?UTF-8?q?=20to=20=E2=80=94-exclude?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 - README.md | 2 +- bin/cli.js | 16 ++++++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ee4fd87..3e75a5be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ So I decided it was high time for version 1.0 to be released and take the opport * Renamed many of the settings in the [config](README.md#configuration) * Option `--json` should now be used instead of `--output json` -* Option `--exclude` renamed to `--exclude-regexp` * The generation of the dependency tree is now delegated to the external module [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree) * A single file will now be used as an entry instead of scanning entire folder(s) * Dependencies will now be extracted recursively from the single file diff --git a/README.md b/README.md index aa031d79..45d0f04c 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ $ madge --depends 'wheels' path/src/app.js > Excluding modules ```sh -$ madge --exclude-regexp '^(foo|bar)$' path/src/app.js +$ madge --exclude '^(foo|bar)$' path/src/app.js ``` > Save graph as a SVG image (graphviz required) diff --git a/bin/cli.js b/bin/cli.js index f26d69dc..74334cd3 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -12,13 +12,13 @@ const version = require('../package.json').version; program .version(version) .usage('[options] [file|dir]') - .option('--basedir ', 'base directory to use when resolving paths') - .option('--list', 'show list of all dependencies (default)') - .option('--summary', 'show summary of all dependencies') + .option('--basedir ', 'base directory for resolving paths') + .option('--list', 'show dependency list (default)') + .option('--summary', 'show dependency count summary') .option('--circular', 'show circular dependencies') - .option('--depends ', 'show modules that depends on the given id') - .option('--json', 'show output as JSON') - .option('--exclude-regexp ', 'exclude modules using a RegExp') + .option('--depends ', 'show module dependents') + .option('--exclude ', 'exclude modules using RegExp') + .option('--json', 'output as JSON') .option('--image ', 'write graph to file as an image') .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') @@ -60,8 +60,8 @@ if (program.basedir) { config.baseDir = program.basedir; } -if (program.excludeRegexp) { - config.excludeRegExp = [program.excludeRegexp]; +if (program.exclude) { + config.excludeRegExp = [program.exclude]; } if (!program.color) { From 77d0a751eebc89111b690da3f53ec3699bd206bb Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 16 Aug 2016 08:29:29 +0200 Subject: [PATCH 33/39] Bump dependency-tree for perf fixes --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 24273497..283b67d7 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "chalk": "^1.1.3", "commander": "^2.9.0", "debug": "^2.2.0", - "dependency-tree": "^5.4.1", + "dependency-tree": "^5.5.0", "graphviz": "^0.0.8", "mz": "^2.4.0", "rc": "^1.1.6", From 07a36edb65d84a84096ef3bb7228f4b9448df37d Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 16 Aug 2016 09:14:04 +0200 Subject: [PATCH 34/39] Move tree generation to tree.js and add support folders --- CHANGELOG.md | 4 +- README.md | 34 ++- bin/cli.js | 55 ++--- lib/api.js | 158 ++----------- lib/tree.js | 215 ++++++++++++++++++ package.json | 9 +- test/api.js | 54 ++++- test/files/cjs/normal/fancy-main/not-index.js | 1 - test/files/cjs/normal/fancy-main/package.json | 3 - 9 files changed, 331 insertions(+), 202 deletions(-) create mode 100644 lib/tree.js delete mode 100644 test/files/cjs/normal/fancy-main/not-index.js delete mode 100644 test/files/cjs/normal/fancy-main/package.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e75a5be..14a0a75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,7 @@ So I decided it was high time for version 1.0 to be released and take the opport * Renamed many of the settings in the [config](README.md#configuration) * Option `--json` should now be used instead of `--output json` * The generation of the dependency tree is now delegated to the external module [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree) -* A single file will now be used as an entry instead of scanning entire folder(s) -* Dependencies will now be extracted recursively from the single file +* Dependencies will now be extracted recursively * NPM installed dependencies are now excluded by default * Node.js core modules are now excluded * The [API](README.md#api) is now using promises @@ -33,7 +32,6 @@ So I decided it was high time for version 1.0 to be released and take the opport * Option `--read` * Option `--find-nested-dependencies` * Option `--paths` -* Option `--extensions` * Option `--config` * Option `--output` * Option `--break-on-error` diff --git a/README.md b/README.md index 45d0f04c..05670702 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ * Works for JavaScript (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus) * NPM installed dependencies are excluded by default (can be enabled in config) * All core Node.js modules (assert, path, fs, etc) are excluded -* Get default file to scan from package.json (bin or main) Read the [changelog](CHANGELOG.md) for latest changes. @@ -58,11 +57,13 @@ $ apt-get install graphviz # API -## madge(filePath: string, config: object) +## madge(path: string|array, config: object) + +> `path` is a single file or directory to read (or an array of files/directories). > `config` is optional and should be [configuration](#configuration) to be used. -Returns a `Promise` resolved with the Madge instance object. +> Returns a `Promise` resolved with the Madge instance object. ## Functions @@ -134,6 +135,7 @@ Property | Type | Default | Description --- | --- | --- | --- `baseDir` | String | null | Base directory to use when resolving paths (defaults to `filePath` directory) `includeNpm` | Boolean | false | If node_modules should be included +`fileExtensions` | Array | ['js'] | Valid file extensions used when scanning a folder for files `showFileExtension` | Boolean | false | If file extensions should be included in module name `excludeRegExp` | Array | false | An array of RegExp to use for excluding modules from the graph `requireConfig` | String | null | RequireJS config for resolving aliased modules @@ -161,12 +163,36 @@ Property | Type | Default | Description ## Examples -> List all module dependencies +> List dependencies from a single file ```sh $ madge path/src/app.js ``` +> List dependencies from multiple files + +```sh +$ madge path/src/foo.js path/src/bar.js +``` + +> List dependencies from all *.js files found in a directory + +```sh +$ madge path/src +``` + +> List dependencies from multiple directories + +```sh +$ madge path/src/foo path/src/bar +``` + +> List dependencies from all *.js and *.jsx files found in a directory + +```sh +$ madge --extensions js,jsx path/src +``` + > Finding circular dependencies ```sh diff --git a/bin/cli.js b/bin/cli.js index 74334cd3..f45f7122 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -2,16 +2,13 @@ 'use strict'; const process = require('process'); -const path = require('path'); -const fs = require('mz/fs'); const program = require('commander'); const rc = require('rc')('madge'); -const readPackage = require('read-package-json'); const version = require('../package.json').version; program .version(version) - .usage('[options] [file|dir]') + .usage('[options] ') .option('--basedir ', 'base directory for resolving paths') .option('--list', 'show dependency list (default)') .option('--summary', 'show dependency count summary') @@ -22,12 +19,19 @@ program .option('--image ', 'write graph to file as an image') .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') + .option('--extensions ', 'comma separated string of valid file extensions') + .option('--show-extension', 'include file extension in module name', false) .option('--require-config ', 'path to RequireJS config') .option('--webpack-config ', 'path to webpack config') .option('--no-color', 'disable color in output and image', false) .option('--debug', 'turn on debug output', false) .parse(process.argv); +if (!program.args.length) { + console.log(program.helpInformation()); + process.exit(1); +} + if (program.debug) { process.env.DEBUG = '*'; } @@ -39,7 +43,6 @@ if (!program.color) { const log = require('../lib/log'); const output = require('../lib/output'); const madge = require('../lib/api'); -const target = program.args[0] || process.cwd(); const config = Object.assign({}, rc); delete config._; @@ -47,7 +50,7 @@ delete config.config; delete config.configs; if (rc.config) { - log('using runtime configuration from %s', rc.config); + log('using runtime config %s', rc.config); } ['layout', 'requireConfig', 'webpackConfig'].forEach((option) => { @@ -64,6 +67,14 @@ if (program.exclude) { config.excludeRegExp = [program.exclude]; } +if (program.extensions) { + config.fileExtensions = program.extensions.split(',').map((s) => s.trim()); +} + +if (program.showExtension) { + config.showFileExtension = true; +} + if (!program.color) { config.backgroundColor = '#ffffff'; config.nodeColor = '#00000'; @@ -72,37 +83,7 @@ if (!program.color) { config.edgeColor = '#757575'; } -fs - .stat(target) - .then((stats) => { - if (stats.isFile()) { - return program.args[0]; - } - - const pkgPath = path.join(target, 'package.json'); - - return new Promise((resolve, reject) => { - readPackage(pkgPath, (err, pkg) => { - if (err) { - reject('Could not read ' + pkgPath + '. Choose another directory or specify file.'); - return; - } - - config.baseDir = target; - - if (pkg.bin) { - log('extracted file path from "bin" entry in ' + pkgPath); - resolve(path.join(target, pkg.bin[Object.keys(pkg.bin)[0]])); - } else if (pkg.main) { - log('extracted file path from "main" entry in ' + pkgPath); - resolve(path.join(target, pkg.main)); - } else { - reject('Could not get file to scan by reading package.json. Update package.json or specify a file.'); - } - }); - }); - }) - .then((filePath) => madge(filePath, config)) +madge(program.args, config) .then((res) => { if (program.summary) { return output.summary(res.obj(), { diff --git a/lib/api.js b/lib/api.js index da045fb8..68ffdf48 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,15 +1,13 @@ 'use strict'; -const path = require('path'); -const fs = require('mz/fs'); -const log = require('./log'); -const dependencyTree = require('dependency-tree'); +const tree = require('./tree'); const cyclic = require('./cyclic'); const graph = require('./graph'); const defaultConfig = { baseDir: null, excludeRegExp: false, + extensions: ['js'], showFileExtension: false, includeNpm: false, requireConfig: null, @@ -25,152 +23,30 @@ const defaultConfig = { graphVizPath: false }; -/** - * Exclude modules from tree using RegExp. - * @param {Object} tree - * @param {Array} excludeRegExp - * @return {Object} - */ -function excludeFromTreeByRegExp(tree, excludeRegExp) { - const regExpList = excludeRegExp.map((re) => new RegExp(re)); - - function regExpFilter(id) { - return regExpList.findIndex((regexp) => regexp.test(id)) < 0; - } - - return Object - .keys(tree) - .filter(regExpFilter) - .reduce((acc, id) => { - acc[id] = tree[id].filter(regExpFilter); - return acc; - }, {}); -} - -/** - * Sort given tree. - * @param {Object} tree - * @return {Object} - */ -function sortDependencies(tree) { - return Object - .keys(tree) - .sort() - .reduce((acc, id) => { - acc[id] = tree[id].sort(); - return acc; - }, {}); -} - class Madge { /** * Class constructor. * @constructor * @api public - * @param {String} filePath + * @param {String|Array} path * @param {Object} config - */ - constructor(filePath, config) { - if (!filePath) { - throw new Error('Filename argument is missing'); - } - - this.config = Object.assign({}, defaultConfig, config); - log('using config %o', this.config); - - this.filePath = path.resolve(filePath); - log('using file path: %s', this.filePath); - - this.baseDir = path.resolve(this.config.baseDir || path.dirname(filePath)); - log('using base directory: %s', this.baseDir); - } - - /** - * Will start parsing filename and compute dependencies. * @return {Promise} */ - parse() { - return fs - .exists(this.filePath) - .then((exists) => { - if (!exists) { - throw new Error('Filename ' + this.filePath + ' does not exists'); - } - - return fs.stat(this.filePath); - }) - .then((stats) => { - if (!stats.isFile()) { - throw new Error('Filename ' + this.filePath + ' is not a file'); - } - }) - .then(() => { - this.tree = this.convertDependencyTree(dependencyTree({ - filename: this.filePath, - directory: this.baseDir, - requireConfig: this.config.requireConfig, - webpackConfig: this.config.webpackConfig, - filter: this.pathFilter.bind(this) - })); - - if (this.config.excludeRegExp) { - this.tree = excludeFromTreeByRegExp(this.tree, this.config.excludeRegExp); - } - - this.tree = sortDependencies(this.tree); - - return this; - }); - } - - /** - * Convert deep tree produced by `dependency-tree` to internal format used by Madge. - * @param {Object} tree - * @param {Object} [graph] - * @return {Object} - */ - convertDependencyTree(tree, graph) { - graph = graph || {}; - - Object - .keys(tree) - .forEach((key) => { - const id = this.processPath(key); - - if (!graph[id]) { - graph[id] = Object - .keys(tree[key]) - .map((dep) => this.processPath(dep)); - } - - this.convertDependencyTree(tree[key], graph); - }); - - return graph; - } - - /** - * Process path. - * @param {String} absPath - * @return {String} - */ - processPath(absPath) { - absPath = path.relative(this.baseDir, absPath); + constructor(path, config) { + if (!path) { + throw new Error('path argument not provided'); + } - if (!this.config.showFileExtension) { - absPath = absPath.replace(/\.\w+$/, ''); + if (typeof path === 'string') { + path = [path]; } - return absPath; - } + this.config = Object.assign({}, defaultConfig, config); - /** - * Function used to determine if a module (and its subtree) should be included in the dependency tree. - * @param {String} path - * @return {Boolean} - */ - pathFilter(path) { - return this.config.includeNpm || path.indexOf('node_modules') < 0; + return tree(path, this.config).then((tree) => { + this.tree = tree; + return this; + }); } /** @@ -229,10 +105,8 @@ class Madge { /** * Expose API. - * @param {String} filePath + * @param {String|Array} path * @param {Object} config * @return {Promise} */ -module.exports = (filePath, config) => { - return new Madge(filePath, config).parse(); -}; +module.exports = (path, config) => new Madge(path, config); diff --git a/lib/tree.js b/lib/tree.js new file mode 100644 index 00000000..2a363f4e --- /dev/null +++ b/lib/tree.js @@ -0,0 +1,215 @@ +'use strict'; + +const fs = require('mz/fs'); +const path = require('path'); +const commondir = require('commondir'); +const walk = require('walkdir'); +const dependencyTree = require('dependency-tree'); +const log = require('./log'); + +class Tree { + /** + * Class constructor. + * @constructor + * @api public + * @param {Array} srcPaths + * @param {Object} config + * @return {Promise} + */ + constructor(srcPaths, config) { + this.srcPaths = srcPaths.map((s) => path.resolve(s)); + log('using src paths %o', this.srcPaths); + + this.config = config; + log('using config %o', this.config); + + return this.getDirs() + .then(this.setBaseDir.bind(this)) + .then(this.getFiles.bind(this)) + .then(this.generateTree.bind(this)); + } + + /** + * Generate the tree from the given files + * @param {Array} files + * @return {Object} + */ + generateTree(files) { + const depTree = {}; + const visited = {}; + + files.forEach((file) => { + if (visited[file]) { + return; + } + + Object.assign(depTree, dependencyTree({ + filename: file, + directory: this.baseDir, + requireConfig: this.config.requireConfig, + webpackConfig: this.config.webpackConfig, + visited: visited, + filter: (path) => { + return this.config.includeNpm || path.indexOf('node_modules') < 0; + } + })); + }); + + let tree = this.flatten(depTree); + + if (this.config.excludeRegExp) { + tree = this.exclude(tree, this.config.excludeRegExp); + } + + tree = this.sort(tree); + + return tree; + } + + /** + * Get directories from the source paths + * @return {Promise} resolved with an array of directories + */ + getDirs() { + return Promise + .all(this.srcPaths.map((srcPath) => { + return fs + .stat(srcPath) + .then((stats) => stats.isDirectory() ? srcPath : path.dirname(path.resolve(srcPath))); + })); + } + + /** + * Get all files found from the source paths + * @return {Promise} resolved with an array of files + */ + getFiles() { + const files = []; + + return Promise + .all(this.srcPaths.map((srcPath) => { + return fs + .stat(srcPath) + .then((stats) => { + if (stats.isFile()) { + files.push(path.resolve(srcPath)); + return; + } + + walk.sync(srcPath, (filePath, stat) => { + if (!stat.isFile()) { + return; + } + + const ext = path.extname(filePath).replace('.', ''); + + if (files.indexOf(filePath) < 0 && this.config.extensions.indexOf(ext) >= 0) { + files.push(filePath); + } + }); + }); + })) + .then(() => files); + } + + /** + * Set the base directory (compute the common one if multiple). + * @param {Array} dirs + */ + setBaseDir(dirs) { + if (this.config.baseDir) { + this.baseDir = path.resolve(this.config.baseDir); + } else { + this.baseDir = commondir(dirs); + } + + log('using base directory %s', this.baseDir); + } + + + /** + * Flatten deep tree produced by `dependency-tree`. + * @param {Object} deepTree + * @param {Object} [tree] + * @return {Object} + */ + flatten(deepTree, tree) { + tree = tree || {}; + + Object + .keys(deepTree) + .forEach((key) => { + const id = this.processPath(key); + + if (!tree[id]) { + tree[id] = Object + .keys(deepTree[key]) + .map((dep) => this.processPath(dep)); + } + + this.flatten(deepTree[key], tree); + }); + + return tree; + } + + /** + * Process path. + * @param {String} absPath + * @return {String} + */ + processPath(absPath) { + absPath = path.relative(this.baseDir, absPath); + + if (!this.config.showFileExtension) { + absPath = absPath.replace(/\.\w+$/, ''); + } + + return absPath; + } + + /** + * Exclude modules from tree using RegExp. + * @param {Object} tree + * @param {Array} excludeRegExp + * @return {Object} + */ + exclude(tree, excludeRegExp) { + const regExpList = excludeRegExp.map((re) => new RegExp(re)); + + function regExpFilter(id) { + return regExpList.findIndex((regexp) => regexp.test(id)) < 0; + } + + return Object + .keys(tree) + .filter(regExpFilter) + .reduce((acc, id) => { + acc[id] = tree[id].filter(regExpFilter); + return acc; + }, {}); + } + + /** + * Sort tree. + * @param {Object} tree + * @return {Object} + */ + sort(tree) { + return Object + .keys(tree) + .sort() + .reduce((acc, id) => { + acc[id] = tree[id].sort(); + return acc; + }, {}); + } +} + + /** + * Expose API. + * @param {Array} srcPaths + * @param {Object} config + * @return {Promise} + */ +module.exports = (srcPaths, config) => new Tree(srcPaths, config); diff --git a/package.json b/package.json index 283b67d7..d25ca241 100644 --- a/package.json +++ b/package.json @@ -31,20 +31,21 @@ "mocha": "mocha test/*.js", "watch": "mocha --watch --growl test/*.js", "lint": "eslint bin/cli.js lib test/*.js", - "debug": "bin/cli.js --debug", + "debug": "bin/cli.js --debug bin lib", "generate": "npm run generate:small && npm run generate:madge", - "generate:small": "bin/cli.js test/files/cjs/circular/a.js --image simple.svg", - "generate:madge": "bin/cli.js --image madge.svg" + "generate:small": "bin/cli.js --image simple.svg test/files/cjs/circular/a.js", + "generate:madge": "bin/cli.js --image madge.svg bin lib" }, "dependencies": { "chalk": "^1.1.3", "commander": "^2.9.0", + "commondir": "^1.0.1", "debug": "^2.2.0", "dependency-tree": "^5.5.0", "graphviz": "^0.0.8", "mz": "^2.4.0", "rc": "^1.1.6", - "read-package-json": "^2.0.4" + "walkdir": "0.0.11" }, "devDependencies": { "@aptoma/eslint-config": "^4.0.0", diff --git a/test/api.js b/test/api.js index 1e9744ef..af3ae414 100644 --- a/test/api.js +++ b/test/api.js @@ -9,26 +9,64 @@ const madge = require('../lib/api'); require('should'); describe('Madge', () => { - it('throws error on missing filename argument', () => { + it('throws error on missing path argument', () => { (() => { madge(); - }).should.throw('Filename argument is missing'); + }).should.throw('path argument not provided'); }); it('returns a Promise', () => { madge(__dirname + '/files/cjs/a.js').should.be.Promise(); // eslint-disable-line new-cap }); - it('throws error if filename argument is not a file', (done) => { - madge(__dirname + '/files').catch((err) => { - err.message.should.match(/is not a file/); + it('throws error if file or directory does not exists', (done) => { + madge(__dirname + '/missing.js').catch((err) => { + err.message.should.match(/no such file or directory/); done(); }).catch(done); }); - it('throws error if file does not exists', (done) => { - madge(__dirname + '/missing.js').catch((err) => { - err.message.should.match(/does not exists/); + it('takes single file as path', (done) => { + madge(__dirname + '/files/cjs/a.js').then((res) => { + res.obj().should.eql({ + 'a': ['b', 'c'], + 'b': ['c'], + 'c': [] + }); + done(); + }).catch(done); + }); + + it('takes an array of files as path and combines the result', (done) => { + madge([__dirname + '/files/cjs/a.js', __dirname + '/files/cjs/normal/d.js']).then((res) => { + res.obj().should.eql({ + 'a': ['b', 'c'], + 'b': ['c'], + 'c': [], + 'normal/d': [] + }); + done(); + }).catch(done); + }); + + it('take a single directory as path and find files in it', (done) => { + madge(__dirname + '/files/cjs/normal').then((res) => { + res.obj().should.eql({ + 'a': ['sub/b'], + 'd': [], + 'sub/b': ['sub/c'], + 'sub/c': ['d'] + }); + done(); + }).catch(done); + }); + + it('takes an array of directories as path and compute the basedir correctly', (done) => { + madge([__dirname + '/files/cjs/multibase/1', __dirname + '/files/cjs/multibase/2']).then((res) => { + res.obj().should.eql({ + '1/a': [], + '2/b': [] + }); done(); }).catch(done); }); diff --git a/test/files/cjs/normal/fancy-main/not-index.js b/test/files/cjs/normal/fancy-main/not-index.js deleted file mode 100644 index 406504df..00000000 --- a/test/files/cjs/normal/fancy-main/not-index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = new Date(); \ No newline at end of file diff --git a/test/files/cjs/normal/fancy-main/package.json b/test/files/cjs/normal/fancy-main/package.json deleted file mode 100644 index 817387e7..00000000 --- a/test/files/cjs/normal/fancy-main/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "main": "not-index.js" -} \ No newline at end of file From f89bf7cf4af6ce83c8e686215b2af81765cdff0c Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Tue, 16 Aug 2016 10:05:57 +0200 Subject: [PATCH 35/39] Update README & CHANGELOG --- CHANGELOG.md | 7 +++---- README.md | 43 +++++++++++++++++++++++-------------------- package.json | 4 ++-- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a0a75e..ccb28c5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG -## v1.0.0 (????) +## v1.0.0 (Aug 19, 2016) After 4 years of adding features/fixes it started to be hard to maintain the project and fix some outstanding issues due to how madge was designed to work. @@ -13,14 +13,13 @@ So I decided it was high time for version 1.0 to be released and take the opport * Reading [config](README.md#configuration) from `.madgerc` (replaces `--config`) * Option `--webpack-config` for supporting aliased module paths * Option `--debug` for turning on debug output -* Get default file to read from package.json **Changed:** * Renamed many of the settings in the [config](README.md#configuration) * Option `--json` should now be used instead of `--output json` * The generation of the dependency tree is now delegated to the external module [dependency-tree](https://github.com/mrjoelkemp/node-dependency-tree) -* Dependencies will now be extracted recursively +* Recurse into child dependencies to get a complete dependency tree of a file * NPM installed dependencies are now excluded by default * Node.js core modules are now excluded * The [API](README.md#api) is now using promises @@ -35,7 +34,7 @@ So I decided it was high time for version 1.0 to be released and take the opport * Option `--config` * Option `--output` * Option `--break-on-error` -* CoffeScript support +* CoffeeScript support * Event callbacks `onParseFile` and `onAddModule` * NPM shrinkwrap no longer used diff --git a/README.md b/README.md index 05670702..18c3d8a8 100644 --- a/README.md +++ b/README.md @@ -12,25 +12,28 @@ * Works for JavaScript (AMD, CommonJS, ES6 modules) and CSS preprocessors (Sass, Stylus) * NPM installed dependencies are excluded by default (can be enabled in config) * All core Node.js modules (assert, path, fs, etc) are excluded +* Recurse into child dependencies to get a complete dependency tree of a file Read the [changelog](CHANGELOG.md) for latest changes. ## Examples -> Simple example of a generated image. +> Graph generated from the madge source code. - - + + -* blue = has dependencies -* green = has no dependencies -* red = has circular dependencies +> A graph with circular dependencies. Blue has dependencies, green has no dependencies, and red has circular dependencies. + + + + -> Example generated from the madge source. +## See it in action - - + + # Installation @@ -133,21 +136,21 @@ madge('path/to/app.js') Property | Type | Default | Description --- | --- | --- | --- -`baseDir` | String | null | Base directory to use when resolving paths (defaults to `filePath` directory) +`baseDir` | String | null | Base directory to use instead of the default `includeNpm` | Boolean | false | If node_modules should be included -`fileExtensions` | Array | ['js'] | Valid file extensions used when scanning a folder for files -`showFileExtension` | Boolean | false | If file extensions should be included in module name -`excludeRegExp` | Array | false | An array of RegExp to use for excluding modules from the graph +`fileExtensions` | Array | ['js'] | Valid file extensions used to find files in directories +`showFileExtension` | Boolean | false | If file extension should be included in module name +`excludeRegExp` | Array | false | An array of RegExp for excluding modules `requireConfig` | String | null | RequireJS config for resolving aliased modules `webpackConfig` | String | null | Webpack config for resolving aliased modules -`layout` | String | dot | Layout to use in graph -`fontName` | String | Arial | Font name to use in graph -`fontSize` | String | 14px | Font size to use in graph +`layout` | String | dot | Layout to use in the graph +`fontName` | String | Arial | Font name to use in the graph +`fontSize` | String | 14px | Font size to use in the graph `backgroundColor` | String | #000000 | Background color for the graph -`nodeColor` | String | #c6c5fe | The default node color to use in the graph -`noDependencyColor` | String | #cfffac | The color to use for nodes with no dependencies -`cyclicNodeColor` | String | #ff6c60 | The color to used for circular dependencies -`edgeColor` | String | #757575 | The edge color to use in the graph +`nodeColor` | String | #c6c5fe | Default node color to use in the graph +`noDependencyColor` | String | #cfffac | Color to use for nodes with no dependencies +`cyclicNodeColor` | String | #ff6c60 | Color to use for circular dependencies +`edgeColor` | String | #757575 | Edge color to use in the graph `graphVizPath` | String | null | Custom GraphViz path > Note that when running the CLI it's possible to use a runtime configuration file. The config should placed in `.madgerc` in your project or home folder. Look [here](https://github.com/dominictarr/rc#standards) for alternative locations for the file. Here's an example: diff --git a/package.json b/package.json index d25ca241..a0cc0fb7 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "lint": "eslint bin/cli.js lib test/*.js", "debug": "bin/cli.js --debug bin lib", "generate": "npm run generate:small && npm run generate:madge", - "generate:small": "bin/cli.js --image simple.svg test/files/cjs/circular/a.js", - "generate:madge": "bin/cli.js --image madge.svg bin lib" + "generate:small": "bin/cli.js --image /tmp/simple.svg test/files/cjs/circular/a.js", + "generate:madge": "bin/cli.js --image /tmp/madge.svg bin lib" }, "dependencies": { "chalk": "^1.1.3", From 0a624c429f5c73a8ecefef3ee1e3636612cdccc9 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 17 Aug 2016 08:43:12 +0200 Subject: [PATCH 36/39] Update Travis config --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 89b96b03..190e37b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,12 @@ node_js: - 4 - 6 -before_install: -- sudo apt-get update -- sudo apt-get install graphviz +sudo: false + +addons: + apt: + packages: + - graphviz script: - npm test From ce9fb7d703153cc6248a09ce145deb40e831082d Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Wed, 17 Aug 2016 16:23:02 +0200 Subject: [PATCH 37/39] Filter out node_modules as early as possible --- lib/tree.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/tree.js b/lib/tree.js index 2a363f4e..c8683158 100644 --- a/lib/tree.js +++ b/lib/tree.js @@ -49,9 +49,7 @@ class Tree { requireConfig: this.config.requireConfig, webpackConfig: this.config.webpackConfig, visited: visited, - filter: (path) => { - return this.config.includeNpm || path.indexOf('node_modules') < 0; - } + filter: this.filterPath.bind(this) })); }); @@ -66,6 +64,15 @@ class Tree { return tree; } + /** + * Filter out some paths from found files + * @param {String} path + * @return {Boolean} + */ + filterPath(path) { + return this.config.includeNpm || path.indexOf('node_modules') < 0; + } + /** * Get directories from the source paths * @return {Promise} resolved with an array of directories @@ -97,7 +104,7 @@ class Tree { } walk.sync(srcPath, (filePath, stat) => { - if (!stat.isFile()) { + if (!this.filterPath(filePath) || !stat.isFile()) { return; } From 318853dd22d1ad7d7c9c269ca059db043e40fbc6 Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Fri, 19 Aug 2016 08:39:35 +0200 Subject: [PATCH 38/39] Increase padding of graph drawing area --- lib/graph.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/graph.js b/lib/graph.js index 3fca9912..0b22cd28 100644 --- a/lib/graph.js +++ b/lib/graph.js @@ -45,6 +45,7 @@ function createGraphvizOptions(config) { return { G: { overlap: false, + pad: 0.111, layout: config.layout, bgcolor: config.backgroundColor }, From 0c209c8fae8bde97006d0c456cc34e1844240fdd Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Fri, 19 Aug 2016 15:10:36 +0200 Subject: [PATCH 39/39] Add short form of some CLI options again --- bin/cli.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index f45f7122..9f317a68 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -9,15 +9,14 @@ const version = require('../package.json').version; program .version(version) .usage('[options] ') - .option('--basedir ', 'base directory for resolving paths') - .option('--list', 'show dependency list (default)') - .option('--summary', 'show dependency count summary') - .option('--circular', 'show circular dependencies') - .option('--depends ', 'show module dependents') - .option('--exclude ', 'exclude modules using RegExp') - .option('--json', 'output as JSON') - .option('--image ', 'write graph to file as an image') - .option('--layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') + .option('-b --basedir ', 'base directory for resolving paths') + .option('-s, --summary', 'show dependency count summary') + .option('-c, --circular', 'show circular dependencies') + .option('-d, --depends ', 'show module dependents') + .option('-x, --exclude ', 'exclude modules using RegExp') + .option('-j, --json', 'output as JSON') + .option('-i, --image ', 'write graph to file as an image') + .option('-l, --layout ', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)') .option('--dot', 'show graph using the DOT language') .option('--extensions ', 'comma separated string of valid file extensions') .option('--show-extension', 'include file extension in module name', false)