UNPKG

5.48 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('--ts-config <file>', 'path to typescript config')
30 .option('--include-npm', 'include shallow NPM modules', false)
31 .option('--no-color', 'disable color in output and image', false)
32 .option('--no-spinner', 'disable progress spinner', false)
33 .option('--stdin', 'read predefined tree from STDIN', false)
34 .option('--warning', 'show warnings about skipped files', false)
35 .option('--debug', 'turn on debug output', false)
36 .parse(process.argv);
37
38if (!program.args.length && !program.stdin) {
39 console.log(program.helpInformation());
40 process.exit(1);
41}
42
43if (program.debug) {
44 process.env.DEBUG = '*';
45}
46
47if (!program.color) {
48 process.env.DEBUG_COLORS = false;
49}
50
51const log = require('../lib/log');
52const output = require('../lib/output');
53const madge = require('../lib/api');
54const config = Object.assign({}, rc);
55
56program.options.forEach((opt) => {
57 const name = opt.name();
58
59 if (program[name]) {
60 config[name] = program[name];
61 }
62});
63
64const spinner = ora({
65 text: 'Finding files',
66 color: 'white',
67 interval: 100000,
68 isEnabled: program.spinner
69});
70
71let exitCode = 0;
72
73delete config._;
74delete config.config;
75delete config.configs;
76
77if (rc.config) {
78 log('using runtime config %s', rc.config);
79}
80
81if (program.basedir) {
82 config.baseDir = program.basedir;
83}
84
85if (program.exclude) {
86 config.excludeRegExp = [program.exclude];
87}
88
89if (program.extensions) {
90 config.fileExtensions = program.extensions.split(',').map((s) => s.trim());
91}
92
93if (program.requireConfig) {
94 config.requireConfig = program.requireConfig;
95}
96
97if (program.webpackConfig) {
98 config.webpackConfig = program.webpackConfig;
99}
100
101if (program.tsConfig) {
102 config.tsConfig = program.tsConfig;
103}
104
105if (program.includeNpm) {
106 config.includeNpm = program.includeNpm;
107}
108
109if (!program.color) {
110 config.backgroundColor = '#ffffff';
111 config.nodeColor = '#000000';
112 config.noDependencyColor = '#000000';
113 config.cyclicNodeColor = '#000000';
114 config.edgeColor = '#757575';
115}
116
117function dependencyFilter() {
118 let prevFile;
119
120 return (dependencyFilePath, traversedFilePath, baseDir) => {
121 if (prevFile !== traversedFilePath) {
122 const relPath = path.relative(baseDir, traversedFilePath);
123 const dir = path.dirname(relPath) + '/';
124 const file = path.basename(relPath);
125
126 if (program.spinner) {
127 spinner.text = chalk.grey(dir) + chalk.cyan(file);
128 spinner.render();
129 }
130 prevFile = traversedFilePath;
131 }
132 };
133}
134
135new Promise((resolve, reject) => {
136 if (program.stdin) {
137 let buffer = '';
138
139 process.stdin
140 .resume()
141 .setEncoding('utf8')
142 .on('data', (chunk) => {
143 buffer += chunk;
144 })
145 .on('end', () => {
146 try {
147 resolve(JSON.parse(buffer));
148 } catch (e) {
149 reject(e);
150 }
151 });
152 } else {
153 resolve(program.args);
154 }
155})
156 .then((src) => {
157 if (!program.json && !program.dot) {
158 spinner.start();
159 config.dependencyFilter = dependencyFilter();
160 }
161
162 return madge(src, config);
163 })
164 .then((res) => {
165 if (!program.json && !program.dot) {
166 spinner.stop();
167 output.getResultSummary(res, startTime);
168 }
169
170 if (program.summary) {
171 output.summary(res.obj(), {
172 json: program.json
173 });
174
175 return res;
176 }
177
178 if (program.depends) {
179 output.modules(res.depends(program.depends), {
180 json: program.json
181 });
182
183 return res;
184 }
185
186 if (program.orphans) {
187 output.modules(res.orphans(), {
188 json: program.json
189 });
190
191 return res;
192 }
193
194 if (program.circular) {
195 const circular = res.circular();
196
197 output.circular(spinner, res, circular, {
198 json: program.json
199 });
200
201 if (circular.length) {
202 exitCode = 1;
203 }
204
205 return res;
206 }
207
208 if (program.image) {
209 return res.image(program.image).then((imagePath) => {
210 spinner.succeed(`${chalk.bold('Image created at')} ${chalk.cyan.bold(imagePath)}`);
211 return res;
212 });
213 }
214
215 if (program.dot) {
216 return res.dot().then((output) => {
217 process.stdout.write(output);
218 return res;
219 });
220 }
221
222 output.list(res.obj(), {
223 json: program.json
224 });
225
226 return res;
227 })
228 .then((res) => {
229 if (program.warning && !program.json) {
230 output.warnings(res);
231 }
232
233 if (!program.json && !program.dot) {
234 console.log('');
235 }
236
237 process.exit(exitCode);
238 })
239 .catch((err) => {
240 spinner.stop();
241 console.log('\n%s %s\n', chalk.red('✖'), err.stack);
242 process.exit(1);
243 });