@datapith/cdk4j-diagram 0.4.1

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.
@@ -0,0 +1,400 @@
1
+ "use strict";
2
+ const jsdom = require("jsdom");
3
+ const { JSDOM } = jsdom;
4
+ const dom = new JSDOM();
5
+ const jsonUtil = require("../resources/JsonUtil");
6
+ const iconMap = require("../resources/IconMap");
7
+ const filterConfig = require("../resources/FilterConfig");
8
+ const fs = require("fs");
9
+ const inquirer = require("inquirer");
10
+ const templateCache = require("../shared/templateCache");
11
+ const openInEditor = require("open-in-editor");
12
+ const prompt = inquirer.createPromptModule();
13
+ const templateHelper = require("../shared/templateParser");
14
+ const actionOption = {
15
+ FilterResourceTypes: "Filter resources by type",
16
+ FilterResourceName: "Filter resources by name",
17
+ EdgeLabels: "Edge labels: On",
18
+ Quit: "Quit",
19
+ };
20
+ const YAML = require("yaml-cfn");
21
+
22
+ global.window = dom.window;
23
+ global.document = window.document;
24
+ global.XMLSerializer = window.XMLSerializer;
25
+ Object.defineProperty(global, 'navigator', {
26
+ value: window.navigator,
27
+ writable: true,
28
+ configurable: true
29
+ });
30
+ const mxgraph = require("mxgraph")({});
31
+ const { mxGraph, mxCodec, mxUtils } = mxgraph;
32
+
33
+ const layouts = [
34
+ { name: "Organic", value: "mxFastOrganicLayout" },
35
+ { name: "Circle", value: "mxCircleLayout" },
36
+ { name: "Compact Tree", value: "mxCompactTreeLayout" },
37
+ { name: "Radial Tree", value: "mxRadialTreeLayout" },
38
+ ];
39
+
40
+ let currentLayout = "mxHierarchicalLayout";
41
+
42
+ let vertices = [];
43
+ let forceLayoutRender = true;
44
+ let locationCache = {};
45
+ let graph = new mxGraph();
46
+ let parent = graph.getDefaultParent();
47
+
48
+ function reset() {
49
+ graph = new mxGraph();
50
+ parent = graph.getDefaultParent();
51
+ vertices = [];
52
+ locationCache = {};
53
+ }
54
+
55
+ function makeGraph(template, prefix = "root") {
56
+ const layout = new mxgraph[currentLayout](graph, true, 500);
57
+ const resources = Object.keys(template.Resources);
58
+ layout.orientation = "west";
59
+ layout.intraCellSpacing = 50;
60
+ layout.interRankCellSpacing = 200;
61
+ layout.interHierarchySpacing = 100;
62
+ layout.parallelEdgeSpacing = 20;
63
+ layout.leftMargin = 200;
64
+ layout.resizeParent = true;
65
+ graph.getModel().beginUpdate();
66
+ try {
67
+ for (const resource of resources) {
68
+ const resObj = template.Resources[resource];
69
+ const type = resObj.Type;
70
+ if (
71
+ (filterConfig.resourceTypesToInclude &&
72
+ filterConfig.resourceNamesToInclude &&
73
+ !filterConfig.resourceTypesToInclude.includes(type)) ||
74
+ !filterConfig.resourceNamesToInclude.includes(resource)
75
+ ) {
76
+ updateFilters(type, resource, prefix);
77
+ continue;
78
+ }
79
+ if (resObj.Template) {
80
+ makeGraph(resObj.Template, resource);
81
+ }
82
+
83
+ const dependencies = getDependencies(template, resource);
84
+ addVertices(resource, dependencies, type, prefix);
85
+ }
86
+
87
+ for (const sourceVertex of vertices) {
88
+ for (const dependencyNode of sourceVertex.dependencies) {
89
+ for (const dependency of dependencyNode.value) {
90
+ const targets = vertices.filter(
91
+ (p) => p.name === prefix + "." + dependency.split(".").pop()
92
+ );
93
+ const targetVertex = targets[0];
94
+ if (!targetVertex) {
95
+ continue;
96
+ }
97
+ let from = sourceVertex.vertex;
98
+ let to = targetVertex.vertex;
99
+ addEdges(from, to, dependencyNode, prefix);
100
+ }
101
+ }
102
+ }
103
+ } catch (err) {
104
+ console.log(err);
105
+ } finally {
106
+ layout.execute(parent);
107
+ forceLayoutRender = false;
108
+ graph.getModel().endUpdate();
109
+ }
110
+ return graph;
111
+ }
112
+
113
+ function addEdges(from, to, dependencyNode) {
114
+ if (from && to) {
115
+ const existingEdges = Object.keys(graph.getModel().cells).filter(
116
+ (c) => c === edgeId(to, from)
117
+ );
118
+
119
+ if (existingEdges.length > 0) {
120
+ const existingEdge = graph.model.cells[existingEdges[0]];
121
+ if (filterConfig.edgeMode === "Off") {
122
+ existingEdge.value = "";
123
+ } else if (
124
+ !existingEdge.value.includes(
125
+ jsonUtil.pathToDescriptor(dependencyNode.path, filterConfig)
126
+ )
127
+ ) {
128
+ existingEdge.value += `\n${jsonUtil.pathToDescriptor(
129
+ dependencyNode.path,
130
+ filterConfig
131
+ )}`;
132
+ }
133
+ return;
134
+ }
135
+ if (dependencyNode.path.indexOf("Properties.Events") > 0) {
136
+ graph.insertEdge(
137
+ parent,
138
+ edgeId(to, from),
139
+ "Invoke",
140
+ to,
141
+ from,
142
+ "edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fillColor=#1ba1e2;strokeColor=#006EAF;labelBackgroundColor=none;fontColor=#7EA6E0;"
143
+ );
144
+ } else {
145
+ graph.insertEdge(
146
+ parent,
147
+ edgeId(to, from),
148
+ jsonUtil.pathToDescriptor(dependencyNode.path, filterConfig),
149
+ from,
150
+ to,
151
+ "edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;fontColor=#EA6B66;"
152
+ );
153
+ }
154
+ }
155
+ }
156
+
157
+ function addVertices(resource, dependencies, type, prefix) {
158
+ if (vertices.filter((p) => p.name === prefix + "." + resource).length === 0) {
159
+ vertices.push({
160
+ name: `${prefix}.${resource}`,
161
+ dependencies: dependencies,
162
+ type: type,
163
+ vertex: graph.insertVertex(
164
+ parent,
165
+ null,
166
+ resource,
167
+ locationCache[resource] ? locationCache[resource].x : 70,
168
+ locationCache[resource] ? locationCache[resource].y : 0,
169
+ 50,
170
+ 50,
171
+ iconMap.getIcon(type)
172
+ ),
173
+ });
174
+ }
175
+ }
176
+
177
+ function getDependencies(template, resource) {
178
+ const dependencies = [];
179
+ jsonUtil.findAllValues(template.Resources[resource], dependencies, "Ref");
180
+ jsonUtil.findAllValues(template.Resources[resource], dependencies, "Fn::Sub");
181
+ jsonUtil.findAllValues(
182
+ template.Resources[resource],
183
+ dependencies,
184
+ "Fn::GetAtt"
185
+ );
186
+
187
+ jsonUtil.findAllValues(
188
+ template.Resources[resource],
189
+ dependencies,
190
+ "Fn::ImportValue"
191
+ );
192
+
193
+ for (const dependency of dependencies) {
194
+ dependency.value = dependency.value.filter((p) => {
195
+ const split = p.split(".");
196
+ if (split.length === 2 && templateCache.templates[split[0]]) {
197
+ return templateCache.templates[split[0]].Resources[split[1]];
198
+ }
199
+ return template.Resources[p];
200
+ });
201
+ }
202
+
203
+ return dependencies;
204
+ }
205
+
206
+ function updateFilters(type, resource, prefix) {
207
+ const cells = graph.getModel().cells;
208
+ const keys = Object.keys(cells);
209
+ keys.map(
210
+ (p) =>
211
+ (locationCache[cells[p].value] = cells[p].geometry
212
+ ? { x: cells[p].geometry.x, y: cells[p].geometry.y }
213
+ : null)
214
+ );
215
+ if (vertices.filter((p) => p.type === type).length) {
216
+ const item = vertices.filter((p) => p.name === `${prefix}.${resource}`)[0];
217
+ if (item) {
218
+ graph.removeCells([item.vertex], true);
219
+ }
220
+ vertices = vertices.filter((p) => p.name != `${prefix}.${resource}`);
221
+ }
222
+ }
223
+
224
+ function edgeId(to, from) {
225
+ return `${to.value}|${from.value}`; //|${pathToDescriptor(dependencyNode.path)}`;
226
+ }
227
+
228
+ function graphToXML(graph) {
229
+ var encoder = new mxCodec();
230
+ var result = encoder.encode(graph.getModel());
231
+ return `<mxfile host="" modified="2020-05-24T15:21:41.060Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.45.0 Chrome/78.0.3904.130 Electron/7.2.4 Safari/537.36" version="13.1.3" etag="lrwgP8mNOWNbAz78NI_h" pages="2">
232
+ <diagram id="diagramid" name="Diagram">
233
+ ${mxUtils.getXml(result)}
234
+ </diagram>
235
+ </mxfile>`;
236
+ }
237
+
238
+ function renderTemplate(template) {
239
+ const xml = graphToXML(makeGraph(template));
240
+ return xml;
241
+ }
242
+
243
+ async function generate(cmd, template) {
244
+ const ciMode = cmd.ciMode;
245
+
246
+ template = template || templateHelper.get(cmd).template;
247
+ jsonUtil.createPseudoResources(template);
248
+
249
+ const resources = iterateResources(template);
250
+ let types = [];
251
+ addTypesToShow(Object.keys(template.Resources), types, template);
252
+ types = [...new Set(types)].sort();
253
+ let resourceTypes = { answer: types };
254
+ let resourceNames = { answer: resources };
255
+ let edgeMode = { answer: "On" };
256
+ filterConfig.resourceNamesToInclude = resourceNames.answer;
257
+ filterConfig.resourceTypesToInclude = resourceTypes.answer;
258
+ filterConfig.edgeMode = edgeMode.answer;
259
+
260
+ let actionChoice = {};
261
+ console.log("Diagram will be written to " + cmd.outputFile);
262
+ let backedUp = false;
263
+ if (fs.existsSync(cmd.outputFile)) {
264
+ fs.copyFileSync(cmd.outputFile, `${cmd.outputFile}.bak`);
265
+ backedUp = true;
266
+ }
267
+
268
+ if (ciMode) {
269
+ if (cmd.excludeTypes && Array.isArray(cmd.excludeTypes)) {
270
+ const filteredTypes = filterConfig.resourceTypesToInclude.filter((type) =>
271
+ shouldFilterFromCiTypeList(type, cmd.excludeTypes)
272
+ );
273
+ filterConfig.resourceTypesToInclude = filteredTypes;
274
+ }
275
+ const xml = renderTemplate(template);
276
+ fs.writeFileSync(cmd.outputFile, xml);
277
+ return;
278
+ }
279
+
280
+ try {
281
+ const editor = await openInEditor.configure({
282
+ editor: "code",
283
+ });
284
+
285
+ await editor.open(cmd.outputFile);
286
+ } catch (err){
287
+ console.log("Could not find vscode");
288
+ }
289
+
290
+ while (true) {
291
+ filterConfig.resourceNamesToInclude = resourceNames.answer;
292
+ filterConfig.resourceTypesToInclude = resourceTypes.answer;
293
+
294
+ filterConfig.edgeMode = edgeMode.answer;
295
+
296
+ const xml = renderTemplate(template);
297
+
298
+ fs.writeFileSync(cmd.outputFile, xml);
299
+
300
+ actionChoice = await prompt({
301
+ message: "Options",
302
+ choices: [
303
+ actionOption.FilterResourceTypes,
304
+ actionOption.FilterResourceName,
305
+ actionOption.EdgeLabels,
306
+ actionOption.Quit,
307
+ ],
308
+ type: "list",
309
+ name: "answer",
310
+ });
311
+
312
+ switch (actionChoice.answer) {
313
+ case actionOption.FilterResourceTypes:
314
+ resourceTypes = await prompt({
315
+ message: "Select resource types to include",
316
+ choices: types,
317
+ default: resourceTypes.answer,
318
+ type: "checkbox",
319
+ name: "answer",
320
+ });
321
+ break;
322
+ case actionOption.FilterResourceName:
323
+ resourceNames = await prompt({
324
+ message: "Select resources to include",
325
+ choices: resources,
326
+ default: resourceNames.answer,
327
+ type: "checkbox",
328
+ name: "answer",
329
+ });
330
+ break;
331
+ case actionOption.EdgeLabels:
332
+ edgeMode = await prompt({
333
+ message: "Toggle edge labels",
334
+ choices: ["On", "Off"],
335
+ default: resourceNames.answer,
336
+ type: "list",
337
+ name: "answer",
338
+ });
339
+ actionOption.EdgeLabels = `Edge labels: ${edgeMode.answer}`;
340
+ break;
341
+ case actionOption.Quit:
342
+ const quit = await prompt({
343
+ message: "Do you want to keep your diagram?",
344
+ type: "confirm",
345
+ name: "answer",
346
+ });
347
+ if (!quit.answer) {
348
+ fs.unlinkSync(cmd.outputFile);
349
+ if (backedUp) {
350
+ fs.renameSync(`${cmd.outputFile}.bak`, cmd.outputFile);
351
+ }
352
+ }
353
+ process.exit(0);
354
+ }
355
+ }
356
+ }
357
+
358
+ function iterateResources(template) {
359
+ const resources = [];
360
+ for (const resource of Object.keys(template.Resources)) {
361
+ resources.push(resource);
362
+ if (template.Resources[resource].Template) {
363
+ resources.push(
364
+ ...iterateResources(template.Resources[resource].Template, resource)
365
+ );
366
+ }
367
+ }
368
+ return resources;
369
+ }
370
+
371
+ function addTypesToShow(resources, types, template) {
372
+ for (const resource of resources) {
373
+ types.push(template.Resources[resource].Type);
374
+ if (template.Resources[resource].Template) {
375
+ addTypesToShow(
376
+ Object.keys(template.Resources[resource].Template.Resources),
377
+ types,
378
+ template.Resources[resource].Template
379
+ );
380
+ }
381
+ }
382
+ }
383
+
384
+ function shouldFilterFromCiTypeList(type, excludeList) {
385
+ type = type.toLowerCase();
386
+ const isInExcludeList = excludeList.find(
387
+ (exclude) =>
388
+ exclude.toLowerCase() == type ||
389
+ (type.startsWith("external resource") && type.includes(exclude))
390
+ );
391
+
392
+ return !isInExcludeList;
393
+ }
394
+
395
+ module.exports = {
396
+ renderTemplate,
397
+ layouts,
398
+ reset,
399
+ generate,
400
+ };
package/graph/Vis.js ADDED
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ const jsonUtil = require("../resources/JsonUtil");
3
+ const icons = require("./ui/icons");
4
+ const filterConfig = require("../resources/FilterConfig");
5
+ const tempDirectory = require("temp-dir");
6
+ const templateCache = require("../shared/templateCache");
7
+ const fs = require("fs");
8
+ const path = require("path");
9
+ const open = require("open");
10
+ const ColorHash = require("color-hash");
11
+ const { yamlParse, yamlDump } = require("yaml-cfn");
12
+ const AWSIcon = require("aws-icons-directory");
13
+
14
+ const colorHash = new ColorHash();
15
+ let nodes = [];
16
+ let edges = [];
17
+ let nested = [];
18
+ let types = new Set();
19
+ let useJson;
20
+
21
+ function reset() {
22
+ nodes = [];
23
+ edges = [];
24
+ nested = [];
25
+ types = new Set();
26
+ }
27
+
28
+ function makeGraph(template, prefix, doReset, renderAll) {
29
+ if (doReset) {
30
+ reset();
31
+ }
32
+ jsonUtil.createPseudoResources(template);
33
+
34
+ const resources = Object.keys(template.Resources);
35
+ try {
36
+ for (const resource of resources) {
37
+ const resObj = template.Resources[resource];
38
+ const type = resObj.Type;
39
+ types.add(type);
40
+ if (resObj.Template) {
41
+ nested.push(resource);
42
+ makeGraph(resObj.Template, resource, false, renderAll);
43
+ }
44
+ const dependencies = getDependencies(template, resource);
45
+ addnodes(
46
+ resource,
47
+ dependencies,
48
+ type,
49
+ template.Resources[resource],
50
+ prefix,
51
+ renderAll
52
+ );
53
+ }
54
+
55
+ for (const sourceVertex of nodes) {
56
+ for (const dependencyNode of sourceVertex.dependencies) {
57
+ for (const dependency of dependencyNode.value) {
58
+ const targets = nodes.filter(
59
+ (p) => p.id === prefix + "." + dependency.split(".").pop()
60
+ );
61
+ const targetVertex = targets[0];
62
+ if (!targetVertex) {
63
+ continue;
64
+ }
65
+ let from = sourceVertex.id;
66
+ let to = targetVertex.id;
67
+ addEdges(from, to, dependencyNode, sourceVertex);
68
+ }
69
+ }
70
+ }
71
+ } catch (err) {
72
+ console.log(err);
73
+ } finally {
74
+ }
75
+ return { nodes, edges };
76
+ }
77
+
78
+ function addEdges(from, to, dependencyNode, fromNode) {
79
+ if (from && to) {
80
+ if (dependencyNode.path.indexOf("Properties.Events") > 0) {
81
+ edges.push({
82
+ to: from,
83
+ from: to,
84
+ label: "Invoke",
85
+ });
86
+ } else {
87
+ const descriptor = jsonUtil.pathToDescriptor(
88
+ dependencyNode.path,
89
+ filterConfig
90
+ );
91
+ if (
92
+ edges.filter(
93
+ (p) => p.from === from && p.to === to && p.label === descriptor
94
+ ).length
95
+ ) {
96
+ return;
97
+ }
98
+
99
+ edges.push({
100
+ from,
101
+ to,
102
+ label: descriptor,
103
+ color: {
104
+ color: colorHash.hex(descriptor),
105
+ },
106
+ });
107
+ }
108
+ }
109
+ }
110
+
111
+ function addnodes(
112
+ resource,
113
+ dependencies,
114
+ type,
115
+ resourceObject,
116
+ prefix,
117
+ renderAll
118
+ ) {
119
+ delete resourceObject.Template;
120
+ if (nodes.filter((p) => p.id === resource).length === 0) {
121
+ nodes.push({
122
+ id: `${prefix}.${resource}`,
123
+ dependencies: dependencies,
124
+ prefix: prefix,
125
+ hidden: prefix != "root" && !renderAll,
126
+ type: type,
127
+ label: resource,
128
+ shape: "image",
129
+ image: createImage(type),
130
+ title: `${
131
+ useJson
132
+ ? JSON.stringify(resourceObject, null, 2)
133
+ : yamlDump(resourceObject).replace(/>/g, "").replace(/</g, "")
134
+ }`,
135
+ resource: resourceObject,
136
+ });
137
+ }
138
+ }
139
+
140
+ function createImage(resourceType) {
141
+ const resType = resourceType.startsWith("External") ? resourceType.match(/^.+\((.+)\)/)[1] : resourceType
142
+ var svg =
143
+ AWSIcon.getSVG(resType) ||
144
+ '<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><g> <rect x="15" y="15" fill="#FF9900" width="70" height="70"/> <g> <path fill="#FFFFFF" d="M39.4,47c0,0.6,0.1,1.1,0.2,1.4c0.1,0.4,0.3,0.7,0.5,1.2c0.1,0.1,0.1,0.3,0.1,0.4c0,0.2-0.1,0.3-0.3,0.5 l-1,0.7c-0.2,0.1-0.3,0.1-0.4,0.1c-0.2,0-0.3-0.1-0.5-0.2c-0.2-0.2-0.4-0.5-0.6-0.8c-0.2-0.3-0.3-0.6-0.5-1 c-1.3,1.5-2.9,2.2-4.8,2.2c-1.4,0-2.4-0.4-3.2-1.2c-0.8-0.8-1.2-1.8-1.2-3.1c0-1.4,0.5-2.5,1.5-3.3c1-0.8,2.3-1.3,4-1.3 c0.5,0,1.1,0,1.7,0.1c0.6,0.1,1.2,0.2,1.9,0.4V42c0-1.2-0.3-2.1-0.8-2.6c-0.5-0.5-1.4-0.8-2.6-0.8c-0.6,0-1.1,0.1-1.7,0.2 c-0.6,0.1-1.2,0.3-1.7,0.6c-0.3,0.1-0.5,0.2-0.6,0.2c-0.1,0-0.2,0-0.3,0c-0.2,0-0.3-0.2-0.3-0.5v-0.8c0-0.3,0-0.5,0.1-0.6 c0.1-0.1,0.2-0.2,0.5-0.3c0.6-0.3,1.2-0.5,2-0.7c0.8-0.2,1.6-0.3,2.5-0.3c1.9,0,3.3,0.4,4.2,1.3c0.9,0.9,1.3,2.2,1.3,4V47z M32.8,49.5c0.5,0,1.1-0.1,1.7-0.3c0.6-0.2,1.1-0.5,1.5-1c0.3-0.3,0.4-0.6,0.6-1c0.1-0.4,0.2-0.9,0.2-1.4V45 c-0.5-0.1-1-0.2-1.5-0.3c-0.5-0.1-1-0.1-1.5-0.1c-1.1,0-1.9,0.2-2.4,0.7c-0.5,0.4-0.8,1.1-0.8,1.9c0,0.8,0.2,1.3,0.6,1.7 C31.5,49.3,32,49.5,32.8,49.5z M45.8,51.2c-0.3,0-0.5-0.1-0.6-0.2c-0.1-0.1-0.2-0.3-0.3-0.6L41,37.9c-0.1-0.3-0.1-0.5-0.1-0.7 c0-0.3,0.1-0.4,0.4-0.4h1.6c0.3,0,0.5,0.1,0.6,0.2c0.1,0.1,0.2,0.3,0.3,0.6l2.7,10.7l2.5-10.7c0.1-0.3,0.2-0.5,0.3-0.6 c0.1-0.1,0.3-0.2,0.7-0.2h1.3c0.3,0,0.5,0.1,0.7,0.2c0.1,0.1,0.2,0.3,0.3,0.6l2.6,10.9l2.8-10.9c0.1-0.3,0.2-0.5,0.3-0.6 c0.1-0.1,0.3-0.2,0.6-0.2h1.5c0.3,0,0.4,0.1,0.4,0.4c0,0.1,0,0.2,0,0.3c0,0.1-0.1,0.2-0.1,0.4l-3.9,12.5c-0.1,0.3-0.2,0.5-0.3,0.6 c-0.1,0.1-0.3,0.2-0.6,0.2h-1.4c-0.3,0-0.5-0.1-0.7-0.2c-0.1-0.1-0.2-0.3-0.3-0.7L50.7,40l-2.5,10.4c-0.1,0.3-0.2,0.5-0.3,0.7 c-0.1,0.1-0.4,0.2-0.7,0.2H45.8z M66.6,51.7c-0.9,0-1.7-0.1-2.5-0.3c-0.8-0.2-1.4-0.4-1.9-0.7c-0.3-0.2-0.4-0.3-0.5-0.5 c-0.1-0.2-0.1-0.3-0.1-0.5V49c0-0.3,0.1-0.5,0.4-0.5c0.1,0,0.2,0,0.3,0.1c0.1,0,0.2,0.1,0.4,0.2c0.5,0.2,1.1,0.4,1.8,0.6 c0.6,0.1,1.3,0.2,1.9,0.2c1,0,1.8-0.2,2.4-0.5c0.6-0.4,0.8-0.9,0.8-1.5c0-0.5-0.1-0.8-0.4-1.1c-0.3-0.3-0.8-0.6-1.6-0.9l-2.4-0.7 c-1.2-0.4-2.1-0.9-2.6-1.6c-0.5-0.7-0.8-1.5-0.8-2.4c0-0.7,0.1-1.3,0.4-1.8c0.3-0.5,0.7-1,1.2-1.3c0.5-0.4,1-0.6,1.7-0.8 c0.6-0.2,1.3-0.3,2-0.3c0.4,0,0.7,0,1.1,0.1c0.4,0,0.7,0.1,1,0.2c0.3,0.1,0.6,0.2,0.9,0.3c0.3,0.1,0.5,0.2,0.7,0.3 c0.2,0.1,0.4,0.3,0.5,0.4c0.1,0.1,0.1,0.3,0.1,0.5v0.8c0,0.3-0.1,0.5-0.4,0.5c-0.1,0-0.3-0.1-0.6-0.2c-0.9-0.4-2-0.6-3.1-0.6 c-0.9,0-1.6,0.2-2.2,0.5c-0.5,0.3-0.8,0.8-0.8,1.4c0,0.5,0.2,0.8,0.5,1.1c0.3,0.3,0.9,0.6,1.8,0.9l2.3,0.7c1.2,0.4,2,0.9,2.5,1.6 c0.5,0.7,0.8,1.4,0.8,2.3c0,0.7-0.1,1.3-0.4,1.9c-0.3,0.6-0.7,1-1.2,1.4c-0.5,0.4-1.1,0.7-1.8,0.9C68.2,51.6,67.5,51.7,66.6,51.7z "/> <g> <path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M69.7,59.5c-5.3,3.9-13.1,6-19.7,6c-9.3,0-17.8-3.5-24.1-9.2 c-0.5-0.5-0.1-1.1,0.5-0.7c6.9,4,15.4,6.4,24.1,6.4c5.9,0,12.4-1.2,18.4-3.8C69.8,57.9,70.6,58.9,69.7,59.5z"/> <path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M71.9,57c-0.7-0.9-4.5-0.4-6.2-0.2c-0.5,0.1-0.6-0.4-0.1-0.7 c3.1-2.1,8.1-1.5,8.6-0.8c0.6,0.7-0.2,5.7-3,8.1c-0.4,0.4-0.9,0.2-0.7-0.3C71.2,61.5,72.6,57.9,71.9,57z"/> </g> </g></g></svg>';
145
+ return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg);
146
+ }
147
+
148
+ function getDependencies(template, resource) {
149
+ const dependencies = [];
150
+ jsonUtil.findAllValues(template.Resources[resource], dependencies, "Ref");
151
+ jsonUtil.findAllValues(template.Resources[resource], dependencies, "Fn::Sub");
152
+ jsonUtil.findAllValues(
153
+ template.Resources[resource],
154
+ dependencies,
155
+ "Fn::GetAtt"
156
+ );
157
+ jsonUtil.findAllValues(
158
+ template.Resources[resource],
159
+ dependencies,
160
+ "Fn::ImportValue"
161
+ );
162
+
163
+ for (const dependency of dependencies) {
164
+ dependency.value = dependency.value.filter((p) => {
165
+ const split = p.split(".");
166
+ if (split.length === 2 && templateCache.templates[split[0]]) {
167
+ return templateCache.templates[split[0]].Resources[split[1]];
168
+ }
169
+ return template.Resources[p];
170
+ });
171
+ }
172
+ return dependencies;
173
+ }
174
+
175
+ async function renderTemplate(
176
+ template,
177
+ isJson,
178
+ filePath,
179
+ ciMode,
180
+ reset,
181
+ standaloneIndex,
182
+ renderAll
183
+ ) {
184
+ useJson = isJson;
185
+ const { nodes, edges } = makeGraph(template, "root", reset, renderAll);
186
+ const fileContent = `
187
+ var renderAll = ${renderAll}
188
+ var nodes = new vis.DataSet(${JSON.stringify(nodes)});
189
+ var edges = new vis.DataSet(${JSON.stringify(edges)});
190
+ var nested = ${JSON.stringify(nested.sort())};
191
+ var types = ${JSON.stringify(Array.from(types).sort())};
192
+ var showSidebar = ${!ciMode};
193
+ `;
194
+ const uiPath = filePath || path.join(tempDirectory, "cdk4j-diagram");
195
+ if (!fs.existsSync(uiPath)) {
196
+ fs.mkdirSync(uiPath);
197
+ }
198
+ if (standaloneIndex) {
199
+ fs.readFile(path.join(__dirname, "ui", "index_standalone.template"), 'utf8', function (err, data) {
200
+ if (err) {
201
+ return console.log(err);
202
+ }
203
+ var result = data.replace(/%DATA_JS%/g, fileContent);
204
+
205
+ fs.writeFile(path.join(uiPath, "index.html"), result, 'utf8', function (err) {
206
+ if (err) return console.log(err);
207
+ });
208
+ });
209
+ }
210
+ else
211
+ {
212
+ fs.copyFileSync(
213
+ path.join(__dirname, "ui", "index.html"),
214
+ path.join(uiPath, "index.html")
215
+ );
216
+ fs.copyFileSync(
217
+ path.join(__dirname, "ui", "icons.js"),
218
+ path.join(uiPath, "icons.js")
219
+ );
220
+ fs.writeFileSync(path.join(uiPath, "data.js"), fileContent);
221
+ }
222
+ if (!ciMode) {
223
+ open(path.join(uiPath, "index.html"));
224
+ }
225
+ }
226
+
227
+ module.exports = {
228
+ renderTemplate,
229
+ reset,
230
+ makeGraph
231
+ };