1 | 'use strict';
|
2 |
|
3 | const path_ = require('path');
|
4 | const tree = require('./tree');
|
5 | const cyclic = require('./cyclic');
|
6 | const graph = require('./graph');
|
7 | const log = require('./log');
|
8 |
|
9 | const defaultConfig = {
|
10 | baseDir: null,
|
11 | excludeRegExp: false,
|
12 | fileExtensions: ['js'],
|
13 | includeNpm: false,
|
14 | requireConfig: null,
|
15 | webpackConfig: null,
|
16 | tsConfig: null,
|
17 | rankdir: 'LR',
|
18 | layout: 'dot',
|
19 | fontName: 'Arial',
|
20 | fontSize: '14px',
|
21 | backgroundColor: '#111111',
|
22 | nodeColor: '#c6c5fe',
|
23 | nodeShape: 'box',
|
24 | nodeStyle: 'rounded',
|
25 | noDependencyColor: '#cfffac',
|
26 | cyclicNodeColor: '#ff6c60',
|
27 | edgeColor: '#757575',
|
28 | graphVizOptions: false,
|
29 | graphVizPath: false,
|
30 | dependencyFilter: false
|
31 | };
|
32 |
|
33 | class Madge {
|
34 | |
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | constructor(path, config) {
|
43 | this.skipped = [];
|
44 |
|
45 | if (!path) {
|
46 | throw new Error('path argument not provided');
|
47 | }
|
48 |
|
49 | this.config = Object.assign({}, defaultConfig, config);
|
50 | if (typeof this.config.tsConfig === 'string') {
|
51 | const ts = require('typescript');
|
52 | const tsParsedConfig = ts.readJsonConfigFile(this.config.tsConfig, ts.sys.readFile);
|
53 | const obj = ts.parseJsonSourceFileConfigFileContent(tsParsedConfig, ts.sys, path_.dirname(config.tsConfig));
|
54 | this.config.tsConfig = {
|
55 | ...obj.raw,
|
56 | compilerOptions: obj.options
|
57 | };
|
58 | log('using tsconfig %o', this.config.tsConfig);
|
59 | }
|
60 |
|
61 | if (typeof path === 'object' && !Array.isArray(path)) {
|
62 | this.tree = path;
|
63 | log('using predefined tree %o', path);
|
64 | return Promise.resolve(this);
|
65 | }
|
66 |
|
67 | if (typeof path === 'string') {
|
68 | path = [path];
|
69 | }
|
70 |
|
71 | return tree(path, this.config).then((res) => {
|
72 | this.tree = res.tree;
|
73 | this.skipped = res.skipped;
|
74 | return this;
|
75 | });
|
76 | }
|
77 |
|
78 | |
79 |
|
80 |
|
81 |
|
82 |
|
83 | obj() {
|
84 | return this.tree;
|
85 | }
|
86 |
|
87 | |
88 |
|
89 |
|
90 |
|
91 |
|
92 | warnings() {
|
93 | return {
|
94 | skipped: this.skipped
|
95 | };
|
96 | }
|
97 |
|
98 | |
99 |
|
100 |
|
101 |
|
102 |
|
103 | circular() {
|
104 | return cyclic(this.tree);
|
105 | }
|
106 |
|
107 | |
108 |
|
109 |
|
110 |
|
111 |
|
112 | circularGraph() {
|
113 | const circularDeps = this.circular();
|
114 |
|
115 | return Object.entries(this.obj())
|
116 | .filter(([k]) => circularDeps.some((x) => x.includes(k)))
|
117 | .reduce((acc, [k, v]) => {
|
118 | acc[k] = v.filter((x) => circularDeps.some((y) => y.includes(x)));
|
119 | return acc;
|
120 | }, {});
|
121 | }
|
122 |
|
123 | |
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | depends(id) {
|
130 | const tree = this.obj();
|
131 |
|
132 | return Object
|
133 | .keys(tree)
|
134 | .filter((dep) => tree[dep].indexOf(id) >= 0);
|
135 | }
|
136 |
|
137 | |
138 |
|
139 |
|
140 |
|
141 |
|
142 | orphans() {
|
143 | const tree = this.obj();
|
144 | const map = {};
|
145 |
|
146 | Object
|
147 | .keys(tree)
|
148 | .forEach((dep) => {
|
149 | tree[dep].forEach((id) => {
|
150 | map[id] = true;
|
151 | });
|
152 | });
|
153 |
|
154 | return Object
|
155 | .keys(tree)
|
156 | .filter((dep) => !map[dep]);
|
157 | }
|
158 |
|
159 | |
160 |
|
161 |
|
162 |
|
163 |
|
164 | leaves() {
|
165 | const tree = this.obj();
|
166 | return Object.keys(tree).filter((key) => !tree[key].length);
|
167 | }
|
168 |
|
169 | |
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 | dot(circularOnly) {
|
176 | return graph.dot(
|
177 | circularOnly ? this.circularGraph() : this.obj(),
|
178 | this.circular(),
|
179 | this.config
|
180 | );
|
181 | }
|
182 |
|
183 | |
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 | image(imagePath, circularOnly) {
|
191 | if (!imagePath) {
|
192 | return Promise.reject(new Error('imagePath not provided'));
|
193 | }
|
194 |
|
195 | return graph.image(
|
196 | circularOnly ? this.circularGraph() : this.obj(),
|
197 | this.circular(),
|
198 | imagePath,
|
199 | this.config
|
200 | );
|
201 | }
|
202 |
|
203 | |
204 |
|
205 |
|
206 |
|
207 |
|
208 | svg() {
|
209 | return graph.svg(this.obj(), this.circular(), this.config);
|
210 | }
|
211 | }
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | module.exports = (path, config) => new Madge(path, config);
|