|
| 1 | +var path = require('path'); |
| 2 | + |
| 3 | +var archy = require('archy'); |
| 4 | +var chalk = require('chalk'); |
| 5 | +var resolve = require('enhanced-resolve'); |
| 6 | + |
| 7 | +exports.fromStats = function buildTree(stats, opts) { |
| 8 | + opts = opts || {}; |
| 9 | + opts.packageDescriptors = opts.packageDescriptors || ['package.json', 'bower.json', 'component.json']; |
| 10 | + |
| 11 | + // Build a module dependency tree |
| 12 | + var tree = {}; |
| 13 | + stats.modules.forEach(function(mod) { |
| 14 | + // Ignore origin modules |
| 15 | + if (mod.name.indexOf('~') === -1) { |
| 16 | + return; |
| 17 | + } |
| 18 | + |
| 19 | + // Resolve webpack query string and loaders to retrieve the resource path. |
| 20 | + // webpack dependencies are prepended with (webpack) |
| 21 | + // Module directories are represented by a ~ |
| 22 | + var modulePath = resolve.parse(mod.name).resource.path.replace('(webpack)', './~/webpack'); |
| 23 | + var moduleFullPath = resolve.parse(mod.identifier).resource.path; |
| 24 | + |
| 25 | + // The following code maps every `~/module_name` in `modulePath` to its corresponding |
| 26 | + // `module_folder/module_name` in `moduleFullPath` |
| 27 | + |
| 28 | + // Split at each `~/module_name` and escape for use in a regexp |
| 29 | + var splitParts = modulePath.split(/~\/[^\/]+\//).map(escapeRegExp); |
| 30 | + |
| 31 | + // Find and traverse every `module_folder/module_name` construct in `moduleFullPath` |
| 32 | + var parentTree = tree; |
| 33 | + for (var i = 1; i < splitParts.length; i++) { |
| 34 | + // Remove everything before the module occurrence so that match.index points |
| 35 | + // to the beginning of the module_folder/module_name construct |
| 36 | + var regexpParts = splitParts.slice(i); |
| 37 | + var regexpHead = '([^\/]+\/([^\/]+)\/)'; // match `module_folder/module_name` |
| 38 | + var regexpTail = regexpParts.join('[^\/]+\/[^\/]+\/'); // match the rest of the path |
| 39 | + var regexp = new RegExp(regexpHead + regexpTail + '$'); |
| 40 | + |
| 41 | + var match = moduleFullPath.match(regexp); |
| 42 | + if (!match) { |
| 43 | + // Shouldn't happen |
| 44 | + return; |
| 45 | + } |
| 46 | + |
| 47 | + var packagePath = moduleFullPath.slice(0, match.index + match[1].length); |
| 48 | + |
| 49 | + // Retrieve the child module version from its descriptor file (package.json, bower.json, ...) |
| 50 | + var packageVersion = 'unknown'; |
| 51 | + for (var j = 0; j < packageDescriptors.length; j++) { |
| 52 | + try { |
| 53 | + packageVersion = require(packagePath + packageDescriptors[j]).version; |
| 54 | + break; |
| 55 | + } catch (e) { |
| 56 | + // do nothing |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + var packageName = match[2]; |
| 61 | + var package = parentTree[packageName]; |
| 62 | + if (!package) { |
| 63 | + parentTree[packageName] = package = { |
| 64 | + name: packageName, |
| 65 | + version: packageVersion, |
| 66 | + path: packagePath, |
| 67 | + tree: {} |
| 68 | + }; |
| 69 | + } |
| 70 | + parentTree = package.tree; |
| 71 | + } |
| 72 | + }); |
| 73 | + |
| 74 | + return tree; |
| 75 | +}; |
| 76 | + |
| 77 | +exports.treeToString = function treeToString(tree, opts) { |
| 78 | + opts = opts || {}; |
| 79 | + opts.pretty = opts.pretty || false; |
| 80 | + opts.fullPaths = opts.fullPaths || false; |
| 81 | + |
| 82 | + // Node that will eventually be passed to archy |
| 83 | + var topNode = { |
| 84 | + label: '', |
| 85 | + nodes: [] |
| 86 | + }; |
| 87 | + |
| 88 | + // Recursively build an archy node tree from our package tree |
| 89 | + (function recursiveTreeBuild(tree, node) { |
| 90 | + for (var key in tree) { |
| 91 | + var package = tree[key]; |
| 92 | + var packageName = opts.pretty ? chalk.underline(package.name) : package.name; |
| 93 | + var packagePath = opts.fullPaths ? package.path : path.relative(path.dirname(module.parent.filename), package.path); |
| 94 | + packagePath = opts.pretty ? chalk.grey(packagePath) : packagePath; |
| 95 | + |
| 96 | + var childNode = { |
| 97 | + label: packageName + '@' + package.version + '\n' + packagePath, |
| 98 | + nodes: [] |
| 99 | + }; |
| 100 | + recursiveTreeBuild(package.tree, childNode); |
| 101 | + node.nodes.push(childNode); |
| 102 | + }; |
| 103 | + })(tree, topNode); |
| 104 | + |
| 105 | + return archy(topNode); |
| 106 | +}; |
0 commit comments