@tramvai/module-deps-graph 2.70.0 → 2.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/papi/deps-graph.es.js +105 -0
- package/lib/papi/deps-graph.js +109 -0
- package/lib/server.es.js +2 -235
- package/lib/server.js +2 -235
- package/lib/utils/get-graph.es.js +135 -0
- package/lib/utils/get-graph.js +139 -0
- package/package.json +8 -9
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createPapiMethod } from '@tramvai/papi';
|
|
2
|
+
import { getGraph } from '../utils/get-graph.es.js';
|
|
3
|
+
|
|
4
|
+
const depsGraph = ({ di }) => {
|
|
5
|
+
return createPapiMethod({
|
|
6
|
+
method: 'get',
|
|
7
|
+
path: '/deps-graph',
|
|
8
|
+
async handler({ parsedUrl: { query }, responseManager }) {
|
|
9
|
+
var _a;
|
|
10
|
+
const graphs = getGraph({ di, searchValue: (_a = query.search) !== null && _a !== void 0 ? _a : '' });
|
|
11
|
+
function isLineVisible(line, graph) {
|
|
12
|
+
var _a;
|
|
13
|
+
return (!query.lines && graph) || ((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(line) !== -1;
|
|
14
|
+
}
|
|
15
|
+
responseManager.setHeader('content-type', 'text/html');
|
|
16
|
+
responseManager.setBody(`
|
|
17
|
+
<script src="//d3js.org/d3.v6.min.js"></script>
|
|
18
|
+
<script src="https://unpkg.com/@hpcc-js/wasm@1.4.1/dist/index.min.js"></script>
|
|
19
|
+
<script src="https://unpkg.com/d3-graphviz@4.0.0/build/d3-graphviz.js"></script>
|
|
20
|
+
|
|
21
|
+
<div style="cursor: pointer; text-align: center; background: rgba(170,170,170,.7); position: fixed; right: 10px; top: 10px; width: 30px; height: 30px; font-size: 20px;" onClick="backToTop()">
|
|
22
|
+
🠕
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<h3>Search:</h3>
|
|
26
|
+
<input placeholder="Search module and token names..." value="${query.search || ''}" style="padding: 10px 4px; width: 450px" onkeyup="event.keyCode === 13 && applySearch(this.value)" onblur="applySearch(this.value)"/><br/>
|
|
27
|
+
|
|
28
|
+
<h3>Command Lines:</h3>
|
|
29
|
+
${graphs
|
|
30
|
+
.map(([token, graph]) => {
|
|
31
|
+
var _a;
|
|
32
|
+
return `
|
|
33
|
+
<input id="${token}" type="checkbox" ${((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(token) !== -1 ? 'checked' : ''} onchange="toggleLine('${token}')"/><label ${!graph ? 'style="color: gray"' : ''} for="${token}">${token}</label>
|
|
34
|
+
`;
|
|
35
|
+
})
|
|
36
|
+
.join('<br/>')}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
<h3>Graphs:</h3>
|
|
40
|
+
${graphs
|
|
41
|
+
.map(([token, graph]) => `
|
|
42
|
+
<section style="display: ${isLineVisible(token, graph) ? 'block' : 'none'}">
|
|
43
|
+
<h3>${token}</h3>
|
|
44
|
+
<div id="${token}_graph"></div>
|
|
45
|
+
<hr style="margin: 15px 0;"/>
|
|
46
|
+
</section>
|
|
47
|
+
`)
|
|
48
|
+
.join('')}
|
|
49
|
+
<script>
|
|
50
|
+
window.toggleLine = function(line) {
|
|
51
|
+
const searchParams = new URLSearchParams(location.search)
|
|
52
|
+
const lines = searchParams.get('lines')
|
|
53
|
+
let selected = lines ? lines.split(',') : []
|
|
54
|
+
if (selected.indexOf(line) !== -1) {
|
|
55
|
+
selected.splice(selected.indexOf(line), 1)
|
|
56
|
+
} else {
|
|
57
|
+
selected = selected.concat(line)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
searchParams.set('lines', selected.join(','))
|
|
61
|
+
location.search = searchParams.toString()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
window.applySearch = function(search) {
|
|
65
|
+
const searchParams = new URLSearchParams(location.search)
|
|
66
|
+
searchParams.set('search', search)
|
|
67
|
+
location.search = searchParams.toString()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const searchParams = new URLSearchParams(location.search)
|
|
71
|
+
const selectedLines = searchParams.get('lines')
|
|
72
|
+
const graphs = ${JSON.stringify(graphs)}
|
|
73
|
+
const renderedGraphs = graphs
|
|
74
|
+
.filter(([token]) => {
|
|
75
|
+
return !selectedLines || selectedLines.split(',').find(t => t === token)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
window.backToTop = function() {
|
|
79
|
+
window.scrollTo({
|
|
80
|
+
top: 0,
|
|
81
|
+
left: 0,
|
|
82
|
+
behavior: "smooth"
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
renderedGraphs
|
|
86
|
+
.forEach(([token]) => {
|
|
87
|
+
d3.select('#' + token + '_graph')
|
|
88
|
+
.graphviz()
|
|
89
|
+
.resetZoom(500);
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
renderedGraphs
|
|
94
|
+
.forEach(([token, graph]) => {
|
|
95
|
+
d3.select('#' + token + '_graph')
|
|
96
|
+
.graphviz()
|
|
97
|
+
.renderDot(graph);
|
|
98
|
+
})
|
|
99
|
+
</script>
|
|
100
|
+
`);
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export { depsGraph };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var papi = require('@tramvai/papi');
|
|
6
|
+
var getGraph = require('../utils/get-graph.js');
|
|
7
|
+
|
|
8
|
+
const depsGraph = ({ di }) => {
|
|
9
|
+
return papi.createPapiMethod({
|
|
10
|
+
method: 'get',
|
|
11
|
+
path: '/deps-graph',
|
|
12
|
+
async handler({ parsedUrl: { query }, responseManager }) {
|
|
13
|
+
var _a;
|
|
14
|
+
const graphs = getGraph.getGraph({ di, searchValue: (_a = query.search) !== null && _a !== void 0 ? _a : '' });
|
|
15
|
+
function isLineVisible(line, graph) {
|
|
16
|
+
var _a;
|
|
17
|
+
return (!query.lines && graph) || ((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(line) !== -1;
|
|
18
|
+
}
|
|
19
|
+
responseManager.setHeader('content-type', 'text/html');
|
|
20
|
+
responseManager.setBody(`
|
|
21
|
+
<script src="//d3js.org/d3.v6.min.js"></script>
|
|
22
|
+
<script src="https://unpkg.com/@hpcc-js/wasm@1.4.1/dist/index.min.js"></script>
|
|
23
|
+
<script src="https://unpkg.com/d3-graphviz@4.0.0/build/d3-graphviz.js"></script>
|
|
24
|
+
|
|
25
|
+
<div style="cursor: pointer; text-align: center; background: rgba(170,170,170,.7); position: fixed; right: 10px; top: 10px; width: 30px; height: 30px; font-size: 20px;" onClick="backToTop()">
|
|
26
|
+
🠕
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<h3>Search:</h3>
|
|
30
|
+
<input placeholder="Search module and token names..." value="${query.search || ''}" style="padding: 10px 4px; width: 450px" onkeyup="event.keyCode === 13 && applySearch(this.value)" onblur="applySearch(this.value)"/><br/>
|
|
31
|
+
|
|
32
|
+
<h3>Command Lines:</h3>
|
|
33
|
+
${graphs
|
|
34
|
+
.map(([token, graph]) => {
|
|
35
|
+
var _a;
|
|
36
|
+
return `
|
|
37
|
+
<input id="${token}" type="checkbox" ${((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(token) !== -1 ? 'checked' : ''} onchange="toggleLine('${token}')"/><label ${!graph ? 'style="color: gray"' : ''} for="${token}">${token}</label>
|
|
38
|
+
`;
|
|
39
|
+
})
|
|
40
|
+
.join('<br/>')}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
<h3>Graphs:</h3>
|
|
44
|
+
${graphs
|
|
45
|
+
.map(([token, graph]) => `
|
|
46
|
+
<section style="display: ${isLineVisible(token, graph) ? 'block' : 'none'}">
|
|
47
|
+
<h3>${token}</h3>
|
|
48
|
+
<div id="${token}_graph"></div>
|
|
49
|
+
<hr style="margin: 15px 0;"/>
|
|
50
|
+
</section>
|
|
51
|
+
`)
|
|
52
|
+
.join('')}
|
|
53
|
+
<script>
|
|
54
|
+
window.toggleLine = function(line) {
|
|
55
|
+
const searchParams = new URLSearchParams(location.search)
|
|
56
|
+
const lines = searchParams.get('lines')
|
|
57
|
+
let selected = lines ? lines.split(',') : []
|
|
58
|
+
if (selected.indexOf(line) !== -1) {
|
|
59
|
+
selected.splice(selected.indexOf(line), 1)
|
|
60
|
+
} else {
|
|
61
|
+
selected = selected.concat(line)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
searchParams.set('lines', selected.join(','))
|
|
65
|
+
location.search = searchParams.toString()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
window.applySearch = function(search) {
|
|
69
|
+
const searchParams = new URLSearchParams(location.search)
|
|
70
|
+
searchParams.set('search', search)
|
|
71
|
+
location.search = searchParams.toString()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const searchParams = new URLSearchParams(location.search)
|
|
75
|
+
const selectedLines = searchParams.get('lines')
|
|
76
|
+
const graphs = ${JSON.stringify(graphs)}
|
|
77
|
+
const renderedGraphs = graphs
|
|
78
|
+
.filter(([token]) => {
|
|
79
|
+
return !selectedLines || selectedLines.split(',').find(t => t === token)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
window.backToTop = function() {
|
|
83
|
+
window.scrollTo({
|
|
84
|
+
top: 0,
|
|
85
|
+
left: 0,
|
|
86
|
+
behavior: "smooth"
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
renderedGraphs
|
|
90
|
+
.forEach(([token]) => {
|
|
91
|
+
d3.select('#' + token + '_graph')
|
|
92
|
+
.graphviz()
|
|
93
|
+
.resetZoom(500);
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
renderedGraphs
|
|
98
|
+
.forEach(([token, graph]) => {
|
|
99
|
+
d3.select('#' + token + '_graph')
|
|
100
|
+
.graphviz()
|
|
101
|
+
.renderDot(graph);
|
|
102
|
+
})
|
|
103
|
+
</script>
|
|
104
|
+
`);
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
exports.depsGraph = depsGraph;
|
package/lib/server.es.js
CHANGED
|
@@ -1,241 +1,8 @@
|
|
|
1
1
|
import { __decorate } from 'tslib';
|
|
2
2
|
import { SERVER_MODULE_PAPI_PUBLIC_ROUTE } from '@tramvai/tokens-server';
|
|
3
3
|
import { DI_TOKEN } from '@tinkoff/dippy';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
function traverseGraphUp(node, cb) {
|
|
8
|
-
if (node) {
|
|
9
|
-
cb(node);
|
|
10
|
-
traverseGraphUp(node.parentNode, cb);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
const getGraph = ({ di, searchValue }) => {
|
|
14
|
-
function getDepsFromRecord(container, parentNode, rank, record) {
|
|
15
|
-
const deps = [];
|
|
16
|
-
Object.values(record.resolvedDeps).forEach((k) => {
|
|
17
|
-
const depGraph = buildDepsGraph(container, parentNode, rank,
|
|
18
|
-
// @ts-ignore
|
|
19
|
-
k.token ? k.token.toString() : k.toString());
|
|
20
|
-
if (depGraph) {
|
|
21
|
-
deps.push(depGraph);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return deps;
|
|
25
|
-
}
|
|
26
|
-
function buildDepsGraph(container, parentNode, rank, token, recordOfMulti) {
|
|
27
|
-
var _a, _b;
|
|
28
|
-
const record = recordOfMulti || container.getRecord(Symbol.for(token));
|
|
29
|
-
if (!record) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
const moduleName = record.stack
|
|
33
|
-
? (_a = /\/(module(?:-|s\/)[\w-]*?)\//.exec(record.stack)) === null || _a === void 0 ? void 0 : _a[1].replace('modules/', '')
|
|
34
|
-
: undefined;
|
|
35
|
-
const searchMatch = searchValue &&
|
|
36
|
-
((token && token.indexOf(searchValue) !== -1) ||
|
|
37
|
-
(moduleName && moduleName.indexOf(searchValue) !== -1));
|
|
38
|
-
if (searchMatch) {
|
|
39
|
-
traverseGraphUp(parentNode, (g) => {
|
|
40
|
-
// eslint-disable-next-line no-param-reassign
|
|
41
|
-
g.parentEdgeMatch = true;
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
const node = {
|
|
45
|
-
token,
|
|
46
|
-
moduleName,
|
|
47
|
-
record,
|
|
48
|
-
match: searchMatch,
|
|
49
|
-
childEdgeMatch: parentNode && (parentNode.match || parentNode.childEdgeMatch),
|
|
50
|
-
parentNode,
|
|
51
|
-
deps: [],
|
|
52
|
-
rank,
|
|
53
|
-
multi: !!record.multi,
|
|
54
|
-
multiInstance: !!record.multi || !!recordOfMulti,
|
|
55
|
-
};
|
|
56
|
-
if ((_b = record.multi) === null || _b === void 0 ? void 0 : _b.length) {
|
|
57
|
-
node.deps = record.multi.reduce((acc, r) => acc.concat(buildDepsGraph(container, node, rank + 1, token, r)), []);
|
|
58
|
-
}
|
|
59
|
-
else if (record === null || record === void 0 ? void 0 : record.resolvedDeps) {
|
|
60
|
-
node.deps = getDepsFromRecord(container, node, rank + 1, record);
|
|
61
|
-
}
|
|
62
|
-
return node;
|
|
63
|
-
}
|
|
64
|
-
function formatGraphToDot(rootGraph) {
|
|
65
|
-
const edges = [];
|
|
66
|
-
const nodes = [];
|
|
67
|
-
const groupsRank = {};
|
|
68
|
-
const groupsByRank = {};
|
|
69
|
-
function recurse(parentNodeId, node) {
|
|
70
|
-
const nodeId = node.moduleName ? `${node.token}:${node.moduleName}` : node.token;
|
|
71
|
-
const addNodeAndEdge = !searchValue || node.childEdgeMatch || node.parentEdgeMatch || node.match;
|
|
72
|
-
if (!addNodeAndEdge) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (parentNodeId) {
|
|
76
|
-
if (!edges.find((e) => e.from === parentNodeId && e.to === nodeId)) {
|
|
77
|
-
edges.push({ from: parentNodeId, to: nodeId });
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (!nodes.find((n) => n.id === nodeId)) {
|
|
81
|
-
nodes.push({
|
|
82
|
-
id: nodeId,
|
|
83
|
-
label: node.moduleName ? `<b>${node.moduleName}</b> | ${node.token}` : node.token,
|
|
84
|
-
multiInstance: node.multiInstance,
|
|
85
|
-
match: node.match,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
if (!groupsRank[nodeId] || groupsRank[nodeId] < node.rank) {
|
|
89
|
-
groupsRank[nodeId] = node.rank;
|
|
90
|
-
}
|
|
91
|
-
node.deps.forEach((d) => {
|
|
92
|
-
recurse(node.multi ? parentNodeId : nodeId, d);
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
recurse(null, rootGraph);
|
|
96
|
-
Object.keys(groupsRank).forEach((nodeId) => {
|
|
97
|
-
const rank = groupsRank[nodeId];
|
|
98
|
-
groupsByRank[rank] = groupsByRank[rank] || [];
|
|
99
|
-
groupsByRank[rank].push(nodeId);
|
|
100
|
-
});
|
|
101
|
-
const res = !nodes.length
|
|
102
|
-
? ''
|
|
103
|
-
: `digraph g {
|
|
104
|
-
rankdir = "LR";
|
|
105
|
-
nodesep=0.1;
|
|
106
|
-
ranksep=0.3;
|
|
107
|
-
compound=true;
|
|
108
|
-
concentrate=true;
|
|
109
|
-
center=true;
|
|
110
|
-
node [fontsize = "16", shape = "record", height=0.1, color=lightblue2];
|
|
111
|
-
edge [];
|
|
112
|
-
${nodes
|
|
113
|
-
.map((n) => {
|
|
114
|
-
return `"${n.id}"[label=<${n.label}>]${n.multiInstance ? '[color="gold"]' : ''}${n.match ? '[color="red"]' : ''};`;
|
|
115
|
-
})
|
|
116
|
-
.join('\n')}
|
|
117
|
-
${edges
|
|
118
|
-
.map((e) => {
|
|
119
|
-
return `"${e.from}"->"${e.to}";`;
|
|
120
|
-
})
|
|
121
|
-
.join('\n')}
|
|
122
|
-
${Object.values(groupsByRank)
|
|
123
|
-
.map((ids) => {
|
|
124
|
-
return `{ rank = "same"; ${ids.map((id) => `"${id}"`).join(';')} }`;
|
|
125
|
-
})
|
|
126
|
-
.join('\n')}
|
|
127
|
-
}`;
|
|
128
|
-
return res;
|
|
129
|
-
}
|
|
130
|
-
return Object.values(commandLineListTokens)
|
|
131
|
-
.map((k) => {
|
|
132
|
-
const g = buildDepsGraph(di, null, 0, k.toString());
|
|
133
|
-
return g ? [k.toString(), g] : null;
|
|
134
|
-
})
|
|
135
|
-
.filter(Boolean)
|
|
136
|
-
.map(([k, g]) => [k, formatGraphToDot(g)]);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const depsGraph = ({ di }) => {
|
|
140
|
-
return createPapiMethod({
|
|
141
|
-
method: 'get',
|
|
142
|
-
path: '/deps-graph',
|
|
143
|
-
async handler({ parsedUrl: { query }, responseManager }) {
|
|
144
|
-
var _a;
|
|
145
|
-
const graphs = getGraph({ di, searchValue: (_a = query.search) !== null && _a !== void 0 ? _a : '' });
|
|
146
|
-
function isLineVisible(line, graph) {
|
|
147
|
-
var _a;
|
|
148
|
-
return (!query.lines && graph) || ((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(line) !== -1;
|
|
149
|
-
}
|
|
150
|
-
responseManager.setHeader('content-type', 'text/html');
|
|
151
|
-
responseManager.setBody(`
|
|
152
|
-
<script src="//d3js.org/d3.v6.min.js"></script>
|
|
153
|
-
<script src="https://unpkg.com/@hpcc-js/wasm@1.4.1/dist/index.min.js"></script>
|
|
154
|
-
<script src="https://unpkg.com/d3-graphviz@4.0.0/build/d3-graphviz.js"></script>
|
|
155
|
-
|
|
156
|
-
<div style="cursor: pointer; text-align: center; background: rgba(170,170,170,.7); position: fixed; right: 10px; top: 10px; width: 30px; height: 30px; font-size: 20px;" onClick="backToTop()">
|
|
157
|
-
🠕
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<h3>Search:</h3>
|
|
161
|
-
<input placeholder="Search module and token names..." value="${query.search || ''}" style="padding: 10px 4px; width: 450px" onkeyup="event.keyCode === 13 && applySearch(this.value)" onblur="applySearch(this.value)"/><br/>
|
|
162
|
-
|
|
163
|
-
<h3>Command Lines:</h3>
|
|
164
|
-
${graphs
|
|
165
|
-
.map(([token, graph]) => {
|
|
166
|
-
var _a;
|
|
167
|
-
return `
|
|
168
|
-
<input id="${token}" type="checkbox" ${((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(token) !== -1 ? 'checked' : ''} onchange="toggleLine('${token}')"/><label ${!graph ? 'style="color: gray"' : ''} for="${token}">${token}</label>
|
|
169
|
-
`;
|
|
170
|
-
})
|
|
171
|
-
.join('<br/>')}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
<h3>Graphs:</h3>
|
|
175
|
-
${graphs
|
|
176
|
-
.map(([token, graph]) => `
|
|
177
|
-
<section style="display: ${isLineVisible(token, graph) ? 'block' : 'none'}">
|
|
178
|
-
<h3>${token}</h3>
|
|
179
|
-
<div id="${token}_graph"></div>
|
|
180
|
-
<hr style="margin: 15px 0;"/>
|
|
181
|
-
</section>
|
|
182
|
-
`)
|
|
183
|
-
.join('')}
|
|
184
|
-
<script>
|
|
185
|
-
window.toggleLine = function(line) {
|
|
186
|
-
const searchParams = new URLSearchParams(location.search)
|
|
187
|
-
const lines = searchParams.get('lines')
|
|
188
|
-
let selected = lines ? lines.split(',') : []
|
|
189
|
-
if (selected.indexOf(line) !== -1) {
|
|
190
|
-
selected.splice(selected.indexOf(line), 1)
|
|
191
|
-
} else {
|
|
192
|
-
selected = selected.concat(line)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
searchParams.set('lines', selected.join(','))
|
|
196
|
-
location.search = searchParams.toString()
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
window.applySearch = function(search) {
|
|
200
|
-
const searchParams = new URLSearchParams(location.search)
|
|
201
|
-
searchParams.set('search', search)
|
|
202
|
-
location.search = searchParams.toString()
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const searchParams = new URLSearchParams(location.search)
|
|
206
|
-
const selectedLines = searchParams.get('lines')
|
|
207
|
-
const graphs = ${JSON.stringify(graphs)}
|
|
208
|
-
const renderedGraphs = graphs
|
|
209
|
-
.filter(([token]) => {
|
|
210
|
-
return !selectedLines || selectedLines.split(',').find(t => t === token)
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
window.backToTop = function() {
|
|
214
|
-
window.scrollTo({
|
|
215
|
-
top: 0,
|
|
216
|
-
left: 0,
|
|
217
|
-
behavior: "smooth"
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
renderedGraphs
|
|
221
|
-
.forEach(([token]) => {
|
|
222
|
-
d3.select('#' + token + '_graph')
|
|
223
|
-
.graphviz()
|
|
224
|
-
.resetZoom(500);
|
|
225
|
-
})
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
renderedGraphs
|
|
229
|
-
.forEach(([token, graph]) => {
|
|
230
|
-
d3.select('#' + token + '_graph')
|
|
231
|
-
.graphviz()
|
|
232
|
-
.renderDot(graph);
|
|
233
|
-
})
|
|
234
|
-
</script>
|
|
235
|
-
`);
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
};
|
|
4
|
+
import { Module, provide } from '@tramvai/core';
|
|
5
|
+
import { depsGraph } from './papi/deps-graph.es.js';
|
|
239
6
|
|
|
240
7
|
let DepsGraphModule = class DepsGraphModule {
|
|
241
8
|
};
|
package/lib/server.js
CHANGED
|
@@ -6,240 +6,7 @@ var tslib = require('tslib');
|
|
|
6
6
|
var tokensServer = require('@tramvai/tokens-server');
|
|
7
7
|
var dippy = require('@tinkoff/dippy');
|
|
8
8
|
var core = require('@tramvai/core');
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
function traverseGraphUp(node, cb) {
|
|
12
|
-
if (node) {
|
|
13
|
-
cb(node);
|
|
14
|
-
traverseGraphUp(node.parentNode, cb);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
const getGraph = ({ di, searchValue }) => {
|
|
18
|
-
function getDepsFromRecord(container, parentNode, rank, record) {
|
|
19
|
-
const deps = [];
|
|
20
|
-
Object.values(record.resolvedDeps).forEach((k) => {
|
|
21
|
-
const depGraph = buildDepsGraph(container, parentNode, rank,
|
|
22
|
-
// @ts-ignore
|
|
23
|
-
k.token ? k.token.toString() : k.toString());
|
|
24
|
-
if (depGraph) {
|
|
25
|
-
deps.push(depGraph);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
return deps;
|
|
29
|
-
}
|
|
30
|
-
function buildDepsGraph(container, parentNode, rank, token, recordOfMulti) {
|
|
31
|
-
var _a, _b;
|
|
32
|
-
const record = recordOfMulti || container.getRecord(Symbol.for(token));
|
|
33
|
-
if (!record) {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
const moduleName = record.stack
|
|
37
|
-
? (_a = /\/(module(?:-|s\/)[\w-]*?)\//.exec(record.stack)) === null || _a === void 0 ? void 0 : _a[1].replace('modules/', '')
|
|
38
|
-
: undefined;
|
|
39
|
-
const searchMatch = searchValue &&
|
|
40
|
-
((token && token.indexOf(searchValue) !== -1) ||
|
|
41
|
-
(moduleName && moduleName.indexOf(searchValue) !== -1));
|
|
42
|
-
if (searchMatch) {
|
|
43
|
-
traverseGraphUp(parentNode, (g) => {
|
|
44
|
-
// eslint-disable-next-line no-param-reassign
|
|
45
|
-
g.parentEdgeMatch = true;
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
const node = {
|
|
49
|
-
token,
|
|
50
|
-
moduleName,
|
|
51
|
-
record,
|
|
52
|
-
match: searchMatch,
|
|
53
|
-
childEdgeMatch: parentNode && (parentNode.match || parentNode.childEdgeMatch),
|
|
54
|
-
parentNode,
|
|
55
|
-
deps: [],
|
|
56
|
-
rank,
|
|
57
|
-
multi: !!record.multi,
|
|
58
|
-
multiInstance: !!record.multi || !!recordOfMulti,
|
|
59
|
-
};
|
|
60
|
-
if ((_b = record.multi) === null || _b === void 0 ? void 0 : _b.length) {
|
|
61
|
-
node.deps = record.multi.reduce((acc, r) => acc.concat(buildDepsGraph(container, node, rank + 1, token, r)), []);
|
|
62
|
-
}
|
|
63
|
-
else if (record === null || record === void 0 ? void 0 : record.resolvedDeps) {
|
|
64
|
-
node.deps = getDepsFromRecord(container, node, rank + 1, record);
|
|
65
|
-
}
|
|
66
|
-
return node;
|
|
67
|
-
}
|
|
68
|
-
function formatGraphToDot(rootGraph) {
|
|
69
|
-
const edges = [];
|
|
70
|
-
const nodes = [];
|
|
71
|
-
const groupsRank = {};
|
|
72
|
-
const groupsByRank = {};
|
|
73
|
-
function recurse(parentNodeId, node) {
|
|
74
|
-
const nodeId = node.moduleName ? `${node.token}:${node.moduleName}` : node.token;
|
|
75
|
-
const addNodeAndEdge = !searchValue || node.childEdgeMatch || node.parentEdgeMatch || node.match;
|
|
76
|
-
if (!addNodeAndEdge) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (parentNodeId) {
|
|
80
|
-
if (!edges.find((e) => e.from === parentNodeId && e.to === nodeId)) {
|
|
81
|
-
edges.push({ from: parentNodeId, to: nodeId });
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (!nodes.find((n) => n.id === nodeId)) {
|
|
85
|
-
nodes.push({
|
|
86
|
-
id: nodeId,
|
|
87
|
-
label: node.moduleName ? `<b>${node.moduleName}</b> | ${node.token}` : node.token,
|
|
88
|
-
multiInstance: node.multiInstance,
|
|
89
|
-
match: node.match,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
if (!groupsRank[nodeId] || groupsRank[nodeId] < node.rank) {
|
|
93
|
-
groupsRank[nodeId] = node.rank;
|
|
94
|
-
}
|
|
95
|
-
node.deps.forEach((d) => {
|
|
96
|
-
recurse(node.multi ? parentNodeId : nodeId, d);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
recurse(null, rootGraph);
|
|
100
|
-
Object.keys(groupsRank).forEach((nodeId) => {
|
|
101
|
-
const rank = groupsRank[nodeId];
|
|
102
|
-
groupsByRank[rank] = groupsByRank[rank] || [];
|
|
103
|
-
groupsByRank[rank].push(nodeId);
|
|
104
|
-
});
|
|
105
|
-
const res = !nodes.length
|
|
106
|
-
? ''
|
|
107
|
-
: `digraph g {
|
|
108
|
-
rankdir = "LR";
|
|
109
|
-
nodesep=0.1;
|
|
110
|
-
ranksep=0.3;
|
|
111
|
-
compound=true;
|
|
112
|
-
concentrate=true;
|
|
113
|
-
center=true;
|
|
114
|
-
node [fontsize = "16", shape = "record", height=0.1, color=lightblue2];
|
|
115
|
-
edge [];
|
|
116
|
-
${nodes
|
|
117
|
-
.map((n) => {
|
|
118
|
-
return `"${n.id}"[label=<${n.label}>]${n.multiInstance ? '[color="gold"]' : ''}${n.match ? '[color="red"]' : ''};`;
|
|
119
|
-
})
|
|
120
|
-
.join('\n')}
|
|
121
|
-
${edges
|
|
122
|
-
.map((e) => {
|
|
123
|
-
return `"${e.from}"->"${e.to}";`;
|
|
124
|
-
})
|
|
125
|
-
.join('\n')}
|
|
126
|
-
${Object.values(groupsByRank)
|
|
127
|
-
.map((ids) => {
|
|
128
|
-
return `{ rank = "same"; ${ids.map((id) => `"${id}"`).join(';')} }`;
|
|
129
|
-
})
|
|
130
|
-
.join('\n')}
|
|
131
|
-
}`;
|
|
132
|
-
return res;
|
|
133
|
-
}
|
|
134
|
-
return Object.values(core.commandLineListTokens)
|
|
135
|
-
.map((k) => {
|
|
136
|
-
const g = buildDepsGraph(di, null, 0, k.toString());
|
|
137
|
-
return g ? [k.toString(), g] : null;
|
|
138
|
-
})
|
|
139
|
-
.filter(Boolean)
|
|
140
|
-
.map(([k, g]) => [k, formatGraphToDot(g)]);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const depsGraph = ({ di }) => {
|
|
144
|
-
return papi.createPapiMethod({
|
|
145
|
-
method: 'get',
|
|
146
|
-
path: '/deps-graph',
|
|
147
|
-
async handler({ parsedUrl: { query }, responseManager }) {
|
|
148
|
-
var _a;
|
|
149
|
-
const graphs = getGraph({ di, searchValue: (_a = query.search) !== null && _a !== void 0 ? _a : '' });
|
|
150
|
-
function isLineVisible(line, graph) {
|
|
151
|
-
var _a;
|
|
152
|
-
return (!query.lines && graph) || ((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(line) !== -1;
|
|
153
|
-
}
|
|
154
|
-
responseManager.setHeader('content-type', 'text/html');
|
|
155
|
-
responseManager.setBody(`
|
|
156
|
-
<script src="//d3js.org/d3.v6.min.js"></script>
|
|
157
|
-
<script src="https://unpkg.com/@hpcc-js/wasm@1.4.1/dist/index.min.js"></script>
|
|
158
|
-
<script src="https://unpkg.com/d3-graphviz@4.0.0/build/d3-graphviz.js"></script>
|
|
159
|
-
|
|
160
|
-
<div style="cursor: pointer; text-align: center; background: rgba(170,170,170,.7); position: fixed; right: 10px; top: 10px; width: 30px; height: 30px; font-size: 20px;" onClick="backToTop()">
|
|
161
|
-
🠕
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
<h3>Search:</h3>
|
|
165
|
-
<input placeholder="Search module and token names..." value="${query.search || ''}" style="padding: 10px 4px; width: 450px" onkeyup="event.keyCode === 13 && applySearch(this.value)" onblur="applySearch(this.value)"/><br/>
|
|
166
|
-
|
|
167
|
-
<h3>Command Lines:</h3>
|
|
168
|
-
${graphs
|
|
169
|
-
.map(([token, graph]) => {
|
|
170
|
-
var _a;
|
|
171
|
-
return `
|
|
172
|
-
<input id="${token}" type="checkbox" ${((_a = query.lines) !== null && _a !== void 0 ? _a : '').search(token) !== -1 ? 'checked' : ''} onchange="toggleLine('${token}')"/><label ${!graph ? 'style="color: gray"' : ''} for="${token}">${token}</label>
|
|
173
|
-
`;
|
|
174
|
-
})
|
|
175
|
-
.join('<br/>')}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
<h3>Graphs:</h3>
|
|
179
|
-
${graphs
|
|
180
|
-
.map(([token, graph]) => `
|
|
181
|
-
<section style="display: ${isLineVisible(token, graph) ? 'block' : 'none'}">
|
|
182
|
-
<h3>${token}</h3>
|
|
183
|
-
<div id="${token}_graph"></div>
|
|
184
|
-
<hr style="margin: 15px 0;"/>
|
|
185
|
-
</section>
|
|
186
|
-
`)
|
|
187
|
-
.join('')}
|
|
188
|
-
<script>
|
|
189
|
-
window.toggleLine = function(line) {
|
|
190
|
-
const searchParams = new URLSearchParams(location.search)
|
|
191
|
-
const lines = searchParams.get('lines')
|
|
192
|
-
let selected = lines ? lines.split(',') : []
|
|
193
|
-
if (selected.indexOf(line) !== -1) {
|
|
194
|
-
selected.splice(selected.indexOf(line), 1)
|
|
195
|
-
} else {
|
|
196
|
-
selected = selected.concat(line)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
searchParams.set('lines', selected.join(','))
|
|
200
|
-
location.search = searchParams.toString()
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
window.applySearch = function(search) {
|
|
204
|
-
const searchParams = new URLSearchParams(location.search)
|
|
205
|
-
searchParams.set('search', search)
|
|
206
|
-
location.search = searchParams.toString()
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const searchParams = new URLSearchParams(location.search)
|
|
210
|
-
const selectedLines = searchParams.get('lines')
|
|
211
|
-
const graphs = ${JSON.stringify(graphs)}
|
|
212
|
-
const renderedGraphs = graphs
|
|
213
|
-
.filter(([token]) => {
|
|
214
|
-
return !selectedLines || selectedLines.split(',').find(t => t === token)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
window.backToTop = function() {
|
|
218
|
-
window.scrollTo({
|
|
219
|
-
top: 0,
|
|
220
|
-
left: 0,
|
|
221
|
-
behavior: "smooth"
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
renderedGraphs
|
|
225
|
-
.forEach(([token]) => {
|
|
226
|
-
d3.select('#' + token + '_graph')
|
|
227
|
-
.graphviz()
|
|
228
|
-
.resetZoom(500);
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
renderedGraphs
|
|
233
|
-
.forEach(([token, graph]) => {
|
|
234
|
-
d3.select('#' + token + '_graph')
|
|
235
|
-
.graphviz()
|
|
236
|
-
.renderDot(graph);
|
|
237
|
-
})
|
|
238
|
-
</script>
|
|
239
|
-
`);
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
};
|
|
9
|
+
var depsGraph = require('./papi/deps-graph.js');
|
|
243
10
|
|
|
244
11
|
exports.DepsGraphModule = class DepsGraphModule {
|
|
245
12
|
};
|
|
@@ -250,7 +17,7 @@ exports.DepsGraphModule = tslib.__decorate([
|
|
|
250
17
|
core.provide({
|
|
251
18
|
provide: tokensServer.SERVER_MODULE_PAPI_PUBLIC_ROUTE,
|
|
252
19
|
multi: true,
|
|
253
|
-
useFactory: depsGraph,
|
|
20
|
+
useFactory: depsGraph.depsGraph,
|
|
254
21
|
deps: {
|
|
255
22
|
di: dippy.DI_TOKEN,
|
|
256
23
|
},
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { commandLineListTokens } from '@tramvai/core';
|
|
2
|
+
|
|
3
|
+
function traverseGraphUp(node, cb) {
|
|
4
|
+
if (node) {
|
|
5
|
+
cb(node);
|
|
6
|
+
traverseGraphUp(node.parentNode, cb);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const getGraph = ({ di, searchValue }) => {
|
|
10
|
+
function getDepsFromRecord(container, parentNode, rank, record) {
|
|
11
|
+
const deps = [];
|
|
12
|
+
Object.values(record.resolvedDeps).forEach((k) => {
|
|
13
|
+
const depGraph = buildDepsGraph(container, parentNode, rank,
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
k.token ? k.token.toString() : k.toString());
|
|
16
|
+
if (depGraph) {
|
|
17
|
+
deps.push(depGraph);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return deps;
|
|
21
|
+
}
|
|
22
|
+
function buildDepsGraph(container, parentNode, rank, token, recordOfMulti) {
|
|
23
|
+
var _a, _b;
|
|
24
|
+
const record = recordOfMulti || container.getRecord(Symbol.for(token));
|
|
25
|
+
if (!record) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const moduleName = record.stack
|
|
29
|
+
? (_a = /\/(module(?:-|s\/)[\w-]*?)\//.exec(record.stack)) === null || _a === void 0 ? void 0 : _a[1].replace('modules/', '')
|
|
30
|
+
: undefined;
|
|
31
|
+
const searchMatch = searchValue &&
|
|
32
|
+
((token && token.indexOf(searchValue) !== -1) ||
|
|
33
|
+
(moduleName && moduleName.indexOf(searchValue) !== -1));
|
|
34
|
+
if (searchMatch) {
|
|
35
|
+
traverseGraphUp(parentNode, (g) => {
|
|
36
|
+
// eslint-disable-next-line no-param-reassign
|
|
37
|
+
g.parentEdgeMatch = true;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const node = {
|
|
41
|
+
token,
|
|
42
|
+
moduleName,
|
|
43
|
+
record,
|
|
44
|
+
match: searchMatch,
|
|
45
|
+
childEdgeMatch: parentNode && (parentNode.match || parentNode.childEdgeMatch),
|
|
46
|
+
parentNode,
|
|
47
|
+
deps: [],
|
|
48
|
+
rank,
|
|
49
|
+
multi: !!record.multi,
|
|
50
|
+
multiInstance: !!record.multi || !!recordOfMulti,
|
|
51
|
+
};
|
|
52
|
+
if ((_b = record.multi) === null || _b === void 0 ? void 0 : _b.length) {
|
|
53
|
+
node.deps = record.multi.reduce((acc, r) => acc.concat(buildDepsGraph(container, node, rank + 1, token, r)), []);
|
|
54
|
+
}
|
|
55
|
+
else if (record === null || record === void 0 ? void 0 : record.resolvedDeps) {
|
|
56
|
+
node.deps = getDepsFromRecord(container, node, rank + 1, record);
|
|
57
|
+
}
|
|
58
|
+
return node;
|
|
59
|
+
}
|
|
60
|
+
function formatGraphToDot(rootGraph) {
|
|
61
|
+
const edges = [];
|
|
62
|
+
const nodes = [];
|
|
63
|
+
const groupsRank = {};
|
|
64
|
+
const groupsByRank = {};
|
|
65
|
+
function recurse(parentNodeId, node) {
|
|
66
|
+
const nodeId = node.moduleName ? `${node.token}:${node.moduleName}` : node.token;
|
|
67
|
+
const addNodeAndEdge = !searchValue || node.childEdgeMatch || node.parentEdgeMatch || node.match;
|
|
68
|
+
if (!addNodeAndEdge) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (parentNodeId) {
|
|
72
|
+
if (!edges.find((e) => e.from === parentNodeId && e.to === nodeId)) {
|
|
73
|
+
edges.push({ from: parentNodeId, to: nodeId });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!nodes.find((n) => n.id === nodeId)) {
|
|
77
|
+
nodes.push({
|
|
78
|
+
id: nodeId,
|
|
79
|
+
label: node.moduleName ? `<b>${node.moduleName}</b> | ${node.token}` : node.token,
|
|
80
|
+
multiInstance: node.multiInstance,
|
|
81
|
+
match: node.match,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (!groupsRank[nodeId] || groupsRank[nodeId] < node.rank) {
|
|
85
|
+
groupsRank[nodeId] = node.rank;
|
|
86
|
+
}
|
|
87
|
+
node.deps.forEach((d) => {
|
|
88
|
+
recurse(node.multi ? parentNodeId : nodeId, d);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
recurse(null, rootGraph);
|
|
92
|
+
Object.keys(groupsRank).forEach((nodeId) => {
|
|
93
|
+
const rank = groupsRank[nodeId];
|
|
94
|
+
groupsByRank[rank] = groupsByRank[rank] || [];
|
|
95
|
+
groupsByRank[rank].push(nodeId);
|
|
96
|
+
});
|
|
97
|
+
const res = !nodes.length
|
|
98
|
+
? ''
|
|
99
|
+
: `digraph g {
|
|
100
|
+
rankdir = "LR";
|
|
101
|
+
nodesep=0.1;
|
|
102
|
+
ranksep=0.3;
|
|
103
|
+
compound=true;
|
|
104
|
+
concentrate=true;
|
|
105
|
+
center=true;
|
|
106
|
+
node [fontsize = "16", shape = "record", height=0.1, color=lightblue2];
|
|
107
|
+
edge [];
|
|
108
|
+
${nodes
|
|
109
|
+
.map((n) => {
|
|
110
|
+
return `"${n.id}"[label=<${n.label}>]${n.multiInstance ? '[color="gold"]' : ''}${n.match ? '[color="red"]' : ''};`;
|
|
111
|
+
})
|
|
112
|
+
.join('\n')}
|
|
113
|
+
${edges
|
|
114
|
+
.map((e) => {
|
|
115
|
+
return `"${e.from}"->"${e.to}";`;
|
|
116
|
+
})
|
|
117
|
+
.join('\n')}
|
|
118
|
+
${Object.values(groupsByRank)
|
|
119
|
+
.map((ids) => {
|
|
120
|
+
return `{ rank = "same"; ${ids.map((id) => `"${id}"`).join(';')} }`;
|
|
121
|
+
})
|
|
122
|
+
.join('\n')}
|
|
123
|
+
}`;
|
|
124
|
+
return res;
|
|
125
|
+
}
|
|
126
|
+
return Object.values(commandLineListTokens)
|
|
127
|
+
.map((k) => {
|
|
128
|
+
const g = buildDepsGraph(di, null, 0, k.toString());
|
|
129
|
+
return g ? [k.toString(), g] : null;
|
|
130
|
+
})
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.map(([k, g]) => [k, formatGraphToDot(g)]);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export { getGraph };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var core = require('@tramvai/core');
|
|
6
|
+
|
|
7
|
+
function traverseGraphUp(node, cb) {
|
|
8
|
+
if (node) {
|
|
9
|
+
cb(node);
|
|
10
|
+
traverseGraphUp(node.parentNode, cb);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const getGraph = ({ di, searchValue }) => {
|
|
14
|
+
function getDepsFromRecord(container, parentNode, rank, record) {
|
|
15
|
+
const deps = [];
|
|
16
|
+
Object.values(record.resolvedDeps).forEach((k) => {
|
|
17
|
+
const depGraph = buildDepsGraph(container, parentNode, rank,
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
k.token ? k.token.toString() : k.toString());
|
|
20
|
+
if (depGraph) {
|
|
21
|
+
deps.push(depGraph);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return deps;
|
|
25
|
+
}
|
|
26
|
+
function buildDepsGraph(container, parentNode, rank, token, recordOfMulti) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
const record = recordOfMulti || container.getRecord(Symbol.for(token));
|
|
29
|
+
if (!record) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const moduleName = record.stack
|
|
33
|
+
? (_a = /\/(module(?:-|s\/)[\w-]*?)\//.exec(record.stack)) === null || _a === void 0 ? void 0 : _a[1].replace('modules/', '')
|
|
34
|
+
: undefined;
|
|
35
|
+
const searchMatch = searchValue &&
|
|
36
|
+
((token && token.indexOf(searchValue) !== -1) ||
|
|
37
|
+
(moduleName && moduleName.indexOf(searchValue) !== -1));
|
|
38
|
+
if (searchMatch) {
|
|
39
|
+
traverseGraphUp(parentNode, (g) => {
|
|
40
|
+
// eslint-disable-next-line no-param-reassign
|
|
41
|
+
g.parentEdgeMatch = true;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const node = {
|
|
45
|
+
token,
|
|
46
|
+
moduleName,
|
|
47
|
+
record,
|
|
48
|
+
match: searchMatch,
|
|
49
|
+
childEdgeMatch: parentNode && (parentNode.match || parentNode.childEdgeMatch),
|
|
50
|
+
parentNode,
|
|
51
|
+
deps: [],
|
|
52
|
+
rank,
|
|
53
|
+
multi: !!record.multi,
|
|
54
|
+
multiInstance: !!record.multi || !!recordOfMulti,
|
|
55
|
+
};
|
|
56
|
+
if ((_b = record.multi) === null || _b === void 0 ? void 0 : _b.length) {
|
|
57
|
+
node.deps = record.multi.reduce((acc, r) => acc.concat(buildDepsGraph(container, node, rank + 1, token, r)), []);
|
|
58
|
+
}
|
|
59
|
+
else if (record === null || record === void 0 ? void 0 : record.resolvedDeps) {
|
|
60
|
+
node.deps = getDepsFromRecord(container, node, rank + 1, record);
|
|
61
|
+
}
|
|
62
|
+
return node;
|
|
63
|
+
}
|
|
64
|
+
function formatGraphToDot(rootGraph) {
|
|
65
|
+
const edges = [];
|
|
66
|
+
const nodes = [];
|
|
67
|
+
const groupsRank = {};
|
|
68
|
+
const groupsByRank = {};
|
|
69
|
+
function recurse(parentNodeId, node) {
|
|
70
|
+
const nodeId = node.moduleName ? `${node.token}:${node.moduleName}` : node.token;
|
|
71
|
+
const addNodeAndEdge = !searchValue || node.childEdgeMatch || node.parentEdgeMatch || node.match;
|
|
72
|
+
if (!addNodeAndEdge) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (parentNodeId) {
|
|
76
|
+
if (!edges.find((e) => e.from === parentNodeId && e.to === nodeId)) {
|
|
77
|
+
edges.push({ from: parentNodeId, to: nodeId });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!nodes.find((n) => n.id === nodeId)) {
|
|
81
|
+
nodes.push({
|
|
82
|
+
id: nodeId,
|
|
83
|
+
label: node.moduleName ? `<b>${node.moduleName}</b> | ${node.token}` : node.token,
|
|
84
|
+
multiInstance: node.multiInstance,
|
|
85
|
+
match: node.match,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (!groupsRank[nodeId] || groupsRank[nodeId] < node.rank) {
|
|
89
|
+
groupsRank[nodeId] = node.rank;
|
|
90
|
+
}
|
|
91
|
+
node.deps.forEach((d) => {
|
|
92
|
+
recurse(node.multi ? parentNodeId : nodeId, d);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
recurse(null, rootGraph);
|
|
96
|
+
Object.keys(groupsRank).forEach((nodeId) => {
|
|
97
|
+
const rank = groupsRank[nodeId];
|
|
98
|
+
groupsByRank[rank] = groupsByRank[rank] || [];
|
|
99
|
+
groupsByRank[rank].push(nodeId);
|
|
100
|
+
});
|
|
101
|
+
const res = !nodes.length
|
|
102
|
+
? ''
|
|
103
|
+
: `digraph g {
|
|
104
|
+
rankdir = "LR";
|
|
105
|
+
nodesep=0.1;
|
|
106
|
+
ranksep=0.3;
|
|
107
|
+
compound=true;
|
|
108
|
+
concentrate=true;
|
|
109
|
+
center=true;
|
|
110
|
+
node [fontsize = "16", shape = "record", height=0.1, color=lightblue2];
|
|
111
|
+
edge [];
|
|
112
|
+
${nodes
|
|
113
|
+
.map((n) => {
|
|
114
|
+
return `"${n.id}"[label=<${n.label}>]${n.multiInstance ? '[color="gold"]' : ''}${n.match ? '[color="red"]' : ''};`;
|
|
115
|
+
})
|
|
116
|
+
.join('\n')}
|
|
117
|
+
${edges
|
|
118
|
+
.map((e) => {
|
|
119
|
+
return `"${e.from}"->"${e.to}";`;
|
|
120
|
+
})
|
|
121
|
+
.join('\n')}
|
|
122
|
+
${Object.values(groupsByRank)
|
|
123
|
+
.map((ids) => {
|
|
124
|
+
return `{ rank = "same"; ${ids.map((id) => `"${id}"`).join(';')} }`;
|
|
125
|
+
})
|
|
126
|
+
.join('\n')}
|
|
127
|
+
}`;
|
|
128
|
+
return res;
|
|
129
|
+
}
|
|
130
|
+
return Object.values(core.commandLineListTokens)
|
|
131
|
+
.map((k) => {
|
|
132
|
+
const g = buildDepsGraph(di, null, 0, k.toString());
|
|
133
|
+
return g ? [k.toString(), g] : null;
|
|
134
|
+
})
|
|
135
|
+
.filter(Boolean)
|
|
136
|
+
.map(([k, g]) => [k, formatGraphToDot(g)]);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
exports.getGraph = getGraph;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-deps-graph",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.72.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"browser": "lib/browser.js",
|
|
6
6
|
"main": "lib/server.js",
|
|
@@ -14,16 +14,15 @@
|
|
|
14
14
|
"url": "git@github.com:Tinkoff/tramvai.git"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "tramvai-build --
|
|
18
|
-
"watch": "tsc -w"
|
|
19
|
-
"build-for-publish": "true"
|
|
17
|
+
"build": "tramvai-build --forPublish --preserveModules",
|
|
18
|
+
"watch": "tsc -w"
|
|
20
19
|
},
|
|
21
20
|
"peerDependencies": {
|
|
22
|
-
"@tramvai/core": "2.
|
|
23
|
-
"@tramvai/tokens-common": "2.
|
|
24
|
-
"@tramvai/tokens-server": "2.
|
|
25
|
-
"@tramvai/papi": "2.
|
|
26
|
-
"@tinkoff/dippy": "0.8.
|
|
21
|
+
"@tramvai/core": "2.72.0",
|
|
22
|
+
"@tramvai/tokens-common": "2.72.0",
|
|
23
|
+
"@tramvai/tokens-server": "2.72.0",
|
|
24
|
+
"@tramvai/papi": "2.72.0",
|
|
25
|
+
"@tinkoff/dippy": "0.8.13",
|
|
27
26
|
"tslib": "^2.4.0"
|
|
28
27
|
},
|
|
29
28
|
"module": "lib/server.es.js",
|