UNPKG

5.24 kBJavaScriptView Raw
1#!/usr/bin/env node
2'use strict';
3
4const path = require('path');
5const process = require('process');
6const program = require('commander');
7const rc = require('rc')('madge');
8const version = require('../package.json').version;
9const ora = require('ora');
10const chalk = require('chalk');
11const startTime = Date.now();
12
13program
14 .version(version)
15 .usage('[options] <src...>')
16 .option('-b, --basedir <path>', 'base directory for resolving paths')
17 .option('-s, --summary', 'show dependency count summary')
18 .option('-c, --circular', 'show circular dependencies')
19 .option('-d, --depends <name>', 'show module dependents')
20 .option('-x, --exclude <regexp>', 'exclude modules using RegExp')
21 .option('-j, --json', 'output as JSON')
22 .option('-i, --image <file>', 'write graph to file as an image')
23 .option('-l, --layout <name>', 'layout engine to use for graph (dot/neato/fdp/sfdp/twopi/circo)')
24 .option('--orphans', 'show modules that no one is depending on')
25 .option('--dot', 'show graph using the DOT language')
26 .option('--extensions <list>', 'comma separated string of valid file extensions')
27 .option('--require-config <file>', 'path to RequireJS config')
28 .option('--webpack-config <file>', 'path to webpack config')
29 .option('--include-npm', 'include shallow NPM modules', false)
30 .option('--no-color', 'disable color in output and image', false)
31 .option('--stdin', 'read predefined tree from STDIN', false)
32 .option('--warning', 'show warnings about skipped files', false)
33 .option('--debug', 'turn on debug output', false)
34 .parse(process.argv);
35
36if (!program.args.length && !program.stdin) {
37 console.log(program.helpInformation());
38 process.exit(1);
39}
40
41if (program.debug) {
42 process.env.DEBUG = '*';
43}
44
45if (!program.color) {
46 process.env.DEBUG_COLORS = false;
47}
48
49const log = require('../lib/log');
50const output = require('../lib/output');
51const madge = require('../lib/api');
52const config = Object.assign({}, rc);
53
54program.options.forEach((opt) => {
55 const name = opt.name();
56
57 if (program[name]) {
58 config[name] = program[name];
59 }
60});
61
62const spinner = ora({
63 text: 'Finding files',
64 color: 'white',
65 interval: 100000
66});
67
68let exitCode = 0;
69
70delete config._;
71delete config.config;
72delete config.configs;
73
74if (rc.config) {
75 log('using runtime config %s', rc.config);
76}
77
78if (program.basedir) {
79 config.baseDir = program.basedir;
80}
81
82if (program.exclude) {
83 config.excludeRegExp = [program.exclude];
84}
85
86if (program.extensions) {
87 config.fileExtensions = program.extensions.split(',').map((s) => s.trim());
88}
89
90if (program.requireConfig) {
91 config.requireConfig = program.requireConfig;
92}
93
94if (program.webpackConfig) {
95 config.webpackConfig = program.webpackConfig;
96}
97
98if (program.includeNpm) {
99 config.includeNpm = program.includeNpm;
100}
101
102if (!program.color) {
103 config.backgroundColor = '#ffffff';
104 config.nodeColor = '#00000';
105 config.noDependencyColor = '#00000';
106 config.cyclicNodeColor = '#000000';
107 config.edgeColor = '#757575';
108}
109
110function dependencyFilter() {
111 let prevFile;
112
113 return (dependencyFilePath, traversedFilePath, baseDir) => {
114 if (prevFile !== traversedFilePath) {
115 const relPath = path.relative(baseDir, traversedFilePath);
116 const dir = path.dirname(relPath) + '/';
117 const file = path.basename(relPath);
118
119 spinner.text = chalk.grey(dir) + chalk.cyan(file);
120 spinner.render();
121 prevFile = traversedFilePath;
122 }
123 };
124}
125
126new Promise((resolve, reject) => {
127 if (program.stdin) {
128 let buffer = '';
129
130 process.stdin
131 .resume()
132 .setEncoding('utf8')
133 .on('data', (chunk) => {
134 buffer += chunk;
135 })
136 .on('end', () => {
137 try {
138 resolve(JSON.parse(buffer));
139 } catch (e) {
140 reject(e);
141 }
142 });
143 } else {
144 resolve(program.args);
145 }
146})
147 .then((src) => {
148 if (!program.json && !program.dot) {
149 spinner.start();
150 config.dependencyFilter = dependencyFilter();
151 }
152
153 return madge(src, config);
154 })
155 .then((res) => {
156 if (!program.json && !program.dot) {
157 spinner.stop();
158 output.getResultSummary(res, startTime);
159 }
160
161 if (program.summary) {
162 output.summary(res.obj(), {
163 json: program.json
164 });
165
166 return res;
167 }
168
169 if (program.depends) {
170 output.modules(res.depends(program.depends), {
171 json: program.json
172 });
173
174 return res;
175 }
176
177 if (program.orphans) {
178 output.modules(res.orphans(), {
179 json: program.json
180 });
181
182 return res;
183 }
184
185 if (program.circular) {
186 const circular = res.circular();
187
188 output.circular(spinner, res, circular, {
189 json: program.json
190 });
191
192 if (circular.length) {
193 exitCode = 1;
194 }
195
196 return res;
197 }
198
199 if (program.image) {
200 return res.image(program.image).then((imagePath) => {
201 spinner.succeed(`${chalk.bold('Image created at')} ${chalk.cyan.bold(imagePath)}`);
202 return res;
203 });
204 }
205
206 if (program.dot) {
207 return res.dot().then((output) => {
208 process.stdout.write(output);
209 return res;
210 });
211 }
212
213 output.list(res.obj(), {
214 json: program.json
215 });
216
217 return res;
218 })
219 .then((res) => {
220 if (program.warning && !program.json) {
221 output.warnings(res);
222 }
223
224 if (!program.json && !program.dot) {
225 console.log('');
226 }
227
228 process.exit(exitCode);
229 })
230 .catch((err) => {
231 spinner.stop();
232 console.log('\n%s %s\n', chalk.red('✖'), err.stack);
233 process.exit(1);
234 });