From 41b54e2ecde61972ea1f2dfd1a75904ec1029d5a Mon Sep 17 00:00:00 2001 From: Patrik Henningsson Date: Fri, 19 Dec 2014 19:27:54 +0100 Subject: [PATCH] Resolve the module IDs from the RequireJS paths-config properly. --- README.md | 1 + lib/madge.js | 69 +++++++++++++++++++++++++++++++++++++++++------- lib/requirejs.js | 15 ++++++++++- test/amd.js | 12 +++------ 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 402a9565..48fb2769 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,7 @@ minimize a global energy function, which is equivalent to statistical multi-dime ## 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) diff --git a/lib/madge.js b/lib/madge.js index 11e3b47a..912fae0e 100644 --- a/lib/madge.js +++ b/lib/madge.js @@ -28,16 +28,63 @@ function mergeTrees(a, b) { }); }); } + /** -* Replace alias found inside tree with alias -* @param {Object} tree -* @param {Object} alias list -*/ -function convertAliases(tree, aliases) { - Object.keys(tree).forEach(function (id) { - tree[id].forEach(function (moduleName, i, array) { - if (aliases[moduleName]) { - array[i] = aliases[moduleName]; + * 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) { + var path, pathDeps, i1, len1, i2, len2, isContained; + + if (baseDir){ + baseDir += '/'; + } else { + baseDir = ''; + } + + Object.keys(pathDefs).forEach(function (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(function (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; + } + } } }); }); @@ -87,8 +134,10 @@ function Madge(src, opts) { } if (this.opts.requireConfig) { + var baseDir = src.length ? src[0].replace(/\\/g, '/') : ''; + baseDir = requirejs.getBaseUrlFromConfig(this.opts.requireConfig, baseDir); + convertPathsToIds(tree, requirejs.getPathsFromConfig(this.opts.requireConfig, this.opts.exclude), baseDir); mergeTrees(tree, requirejs.getShimDepsFromConfig(this.opts.requireConfig, this.opts.exclude)); - convertAliases(tree, requirejs.getPathsFromConfig(this.opts.requireConfig, this.opts.exclude)); } this.tree = tree; diff --git a/lib/requirejs.js b/lib/requirejs.js index 5500b828..f55496b2 100644 --- a/lib/requirejs.js +++ b/lib/requirejs.js @@ -4,12 +4,14 @@ * Module dependencies. */ var fs = require('fs'), - parse = require('./parse/parse'); + parse = require('./parse/parse'), + path = require('path'); /** * Read shim dependencies from RequireJS config. * @param {String} filename * @param {String} [exclude] + * @return {Object} */ module.exports.getShimDepsFromConfig = function (filename, exclude) { var deps = {}, @@ -36,6 +38,7 @@ module.exports.getShimDepsFromConfig = function (filename, exclude) { * Read path definitions from RequireJS config. * @param {String} filename * @param {String} [exclude] +* @return {Object} */ module.exports.getPathsFromConfig = function (filename, exclude) { var paths = {}, @@ -53,3 +56,13 @@ module.exports.getPathsFromConfig = function (filename, exclude) { return paths; }; + +/** + * Read baseUrl from RequireJS config. + * @param {String} filename + * @param {String} srcBaseDir + */ +module.exports.getBaseUrlFromConfig = function (filename, srcBaseDir) { + var config = parse.findConfig(filename, fs.readFileSync(filename, 'utf8')); + return config.baseUrl ? path.relative(srcBaseDir, config.baseUrl) : ''; +}; \ No newline at end of file diff --git a/test/amd.js b/test/amd.js index 84bfb5a1..f52fb4ef 100644 --- a/test/amd.js +++ b/test/amd.js @@ -35,7 +35,7 @@ describe('module format (AMD)', function () { madge([__dirname + '/files/amd/requirejs/a.js'], { format: 'amd', requireConfig: __dirname + '/files/amd/requirejs/config.js' - }).obj().should.eql({ a: [ 'vendor/jquery-2.0.3' ], 'jquery': [], 'jquery.foo': [ 'vendor/jquery-2.0.3' ], 'jquery.bar': [ 'vendor/jquery-2.0.3' ], 'baz': [ 'vendor/quux' ], 'quux': [] }); + }).obj().should.eql({ 'a': [ 'jquery' ], 'jquery': [], 'jquery.foo': [ 'jquery' ], 'jquery.bar': [ 'jquery' ], 'baz': [ 'quux' ], 'quux': [] }); }); it('should be able to exclude modules', function () { @@ -53,7 +53,7 @@ describe('module format (AMD)', function () { format: 'amd', requireConfig: __dirname + '/files/amd/requirejs/config.js', exclude: '^jquery.foo|quux$' - }).obj().should.eql({ a: [ 'vendor/jquery-2.0.3' ], 'jquery': [], 'jquery.bar': [ 'vendor/jquery-2.0.3' ] , 'baz': []}); + }).obj().should.eql({ a: [ 'jquery' ], 'jquery': [], 'jquery.bar': [ 'jquery' ] , 'baz': []}); }); it('should tackle errors in files', function () { @@ -62,12 +62,6 @@ describe('module format (AMD)', function () { }).obj().should.eql({ error: [] }); }); - // it('should handle id different than file', function () { - // madge([__dirname + '/files/amd/namedWrapped/diff.js'], { - // format: 'amd' - // }).obj().should.eql({ 'ffid': [] }); - // }); - it('should handle named modules', function () { madge([__dirname + '/files/amd/namedWrapped/car.js'], { format: 'amd' @@ -90,7 +84,7 @@ describe('module format (AMD)', function () { madge([__dirname + '/files/amd/circularAlias'], { format: 'amd', requireConfig: __dirname + '/files/amd/circularAlias/config.js' - }).circular().getArray().should.eql([ [ 'dos', 'x86' ] ]); + }).circular().getArray().should.eql([ [ 'cpu', 'jsdos' ] ]); }); it('should find modules that depends on another', function () {