UNPKG

19 kBMarkdownView Raw
1> Why? While I've been working on [Under-the-hood-ReactJS](https://github.com/Bogdan-Lyashenko/Under-the-hood-ReactJS) I spent enormous amount of time on creating schemes. Each change in code or flowchart affects all entire scheme instantly, forcing you to move and align 'broken pieces'. Just repeated manual work...
2
3Imagine a library which takes any JS code and generate SVG flowchart from it, works on client and server. Allows you easily adjust styles scheme for your context or demonstrate your code logic from different abstractions levels. Highlighting, destructing whole blocks, custom modifiers for your needs etc.
4
5# js2flowchart.js [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Generate%20beautiful%20flowcharts%20from%20JavaScript&url=https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart&via=bliashenko&hashtags=javascript,flowchart,svg)
6[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php) [![npm version](https://badge.fury.io/js/js2flowchart.svg)](https://badge.fury.io/js/js2flowchart)
7
8js2flowchart is a tool for generating beautiful SVG flowcharts™ from JavaScript code.
9
10To get started install package from NPM
11> yarn add js2flowchart
12
13or try it right away at [codepen sample](https://codepen.io/Bogdan-Lyashenko/pen/XzmzNv), or play with the demo below.
14
15## [Demo](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/live-editor/index.html)
16Check out live [**code editor**](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/live-editor/index.html), paste your code and **download SVG file** of flowchart!
17
18[<img src="/docs/live-editor/demo.gif" width="700">](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/live-editor/index.html)
19
20### What does js2flowchart do?
21js2flowchart takes your JS code and returns SVG flowchart, works on client/server, support ES6.
22
23Main features:
24- **defined abstractions levels** to render only import/exports, classes/function names, function dependencies to learn/explain the code step by step.
25- **custom abstractions levels support** create your own one
26- **presentation generator** to generate list of SVGs in order to different abstractions levels
27- **defined flow tree modifiers** to map well-known APIs like i.e. [].map, [].forEach, [].filter to Loop structure on scheme etc.
28- **destruction modifier** to replace block of code with one shape on scheme
29- **custom flow tree modifiers support** create your own one
30- **flow tree ignore filter** to omit some code nodes completely i.e. log lines
31- **focus node or entire code logic branch** to highlight important section on scheme
32- **blur node or entire code logic branch** to hide less-important stuff
33- **defined styles themes supports** choose one you like
34- **custom themes support** create your own one which fits your context colors better
35- **custom colors and styles support** provides handy API to change specific styles without boilerplate
36
37Use cases:
38- **explain/document** your code by flowcharts
39- **learn** other's code by visual understanding
40- **create** flowcharts for any process simply described by valid JS syntax
41
42### API and examples
43You can find sources for examples explained below in [docs directory](/docs).
44
45**In examples only** js2flowchart library included explicitly, by ```<script>``` tag and accessed by global variable from ```window``` **to make it simpler to run for you without boilerplate**. But feel free to use it through ES6 modules as well, when you have Babel&Webpack local server configured.
46```javascript
47/**
48* Access APIs when js2flowchart injected into HTML page
49*/
50const {convertCodeToFlowTree, convertFlowTreeToSvg} = window.js2flowchart;
51
52/**
53* or import from node_modules
54*/
55import {convertCodeToFlowTree, convertFlowTreeToSvg} from 'js2flowchart';//way 1
56import * as js2flowchart from 'js2flowchart';//way 2
57```
58
59#### Default
60
61Here is a code function for classic case Binary search
62
63```javascript
64const code = `function indexSearch(list, element) {
65 let currentIndex,
66 currentElement,
67 minIndex = 0,
68 maxIndex = list.length - 1;
69
70 while (minIndex <= maxIndex) {
71 currentIndex = Math.floor(minIndex + maxIndex) / 2;
72 currentElement = list[currentIndex];
73
74 if (currentElement === element) {
75 return currentIndex;
76 }
77
78 if (currentElement < element) {
79 minIndex = currentIndex + 1;
80 }
81
82 if (currentElement > element) {
83 maxIndex = currentIndex - 1;
84 }
85 }
86
87 return -1;
88}`;
89```
90let's convert it to SVG(the simplest way):
91```javascript
92const svg = js2flowchart.convertCodeToSvg(code);
93```
94Result:
95
96![](/docs/examples/default/flowchart-image.png)
97
98If you need to modify default behavior you can split ```js2flowchart.convertCodeToSvg``` into two building block:
99- flow tree building
100- shapes printing
101
102```javascript
103const {convertCodeToFlowTree, convertFlowTreeToSvg} = js2flowchart;
104
105const flowTree = convertCodeToFlowTree(code);
106
107const svg = convertFlowTreeToSvg(flowTree);//XML string
108```
109
110or when you need full control create main instances manually:
111```javascript
112const {createFlowTreeBuilder, createSVGRender} = js2flowchart;
113
114const flowTreeBuilder = createFlowTreeBuilder(),
115 svgRender = createSVGRender();
116
117const flowTree = flowTreeBuilder.build(code),
118 shapesTree = svgRender.buildShapesTree(flowTree);
119
120const svg = shapesTree.print();//XML string
121```
122
123See the example running [here](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/default/index.html) or check out complete source code [of it](/docs/examples/default/index.html).
124
125#### Defined abstraction level
126
127What is called 'abstraction level'? Let's say you would like to omit some details, like, e.g. for given module you are interested only in what the module ```exports```, or, what classes it contains.
128There is a list of defined levels you can do that with. Accessible by ```ABSTRACTION_LEVELS``` interface.
129- ```FUNCTION```
130- ```FUNCTION_DEPENDENCIES```
131- ```CLASS```
132- ```IMPORT```
133- ```EXPORT```
134
135Let's take example with module imports&exports. Below is the code of some ```print-util.js```.
136```javascript
137const code = `
138 import {format, trim} from 'formattier';
139 import {log} from 'logger';
140
141 const data = [];
142
143 export default print = (list) => {
144 list.forEach(i => {
145 console.log(i);
146 });
147 }
148
149 export const formatString = (str) => formatter(str);
150 export const MAX_STR_LENGTH = 15;
151`;
152```
153we need to instantiate ```flowTreeBuilder``` and assign abstraction level on it.
154
155```javascript
156 const {
157 ABSTRACTION_LEVELS, createFlowTreeBuilder, convertFlowTreeToSvg
158 } = js2flowchart;
159
160 const flowTreeBuilder = createFlowTreeBuilder();
161
162 //you can pass one level or multiple levels
163 flowTreeBuilder.setAbstractionLevel([
164 ABSTRACTION_LEVELS.IMPORT,
165 ABSTRACTION_LEVELS.EXPORT
166 ]);
167
168 const flowTree = flowTreeBuilder.build(code);
169 const svg = convertFlowTreeToSvg(flowTree);
170```
171
172Result:
173
174![](/docs/examples/defined-abstraction-level/flowchart-image.png)
175
176See the example running [here](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/defined-abstraction-level/index.html) or check out complete source code [of it](/docs/examples/defined-abstraction-level/index.html).
177
178**Custom abstraction level (label:advanced)**
179
180What if you want your 'own' level? To the same API endpoint ```flowTreeBuilder.setAbstractionLevel``` you can provide configuration object.
181For example, have a look at the code of [function dependencies](/src/builder/abstraction-levels/functionDependencies.js) abstraction level.
182Check out the export of it
183```javascript
184export const getFunctionDependenciesLevel = () => ({
185 defined: [TOKEN_TYPES.CALL_EXPRESSION],
186 custom: [
187 getCustomFunctionDeclaration(),
188 getCustomAssignmentExpression(),
189 getCustomVariableDeclarator()
190 ]
191});
192```
193It's a format of data you need to pass:
194
195```javascript
196flowTreeBuilder.setAbstractionLevel({
197 defined: [TOKEN_TYPES.CALL_EXPRESSION],
198 custom: [
199 getCustomFunctionDeclaration(),
200 getCustomAssignmentExpression(),
201 getCustomVariableDeclarator()
202 ]
203})
204
205````
206And what is behind of ```getCustomAssignmentExpression``` for example?
207There is a token parsing config.
208```javascript
209{
210 type: 'TokenType', /*see types in TOKEN_TYPES map*/
211 getName: (path) => {/*extract name from token*/},
212 ignore: (path) => {/*return true if want to omit entry*/}
213 body: true /* should it contain nested blocks? */
214}
215```
216Check out more token parsing configs from [source code (entryDefinitionsMap.js)](/src/builder/entryDefinitionsMap.js)
217
218
219#### Presentation generator
220
221When you learn other's code it's good to go through it by different abstractions levels.
222Take a look what module exports, which function and classes contains etc.
223There is a sub-module ```createPresentationGenerator``` to generate list of SVGs in order to different abstractions levels.
224
225Let's take the next code for example:
226```javascript
227const code = `
228 import {format} from './util/string';
229
230 function formatName(name) {
231 if (!name) return 'no-name';
232
233 return format(name);
234 }
235
236 class Animal {
237 constructor(breed) {
238 this.breed = breed;
239 }
240
241 getBreed() {
242 return this.breed;
243 }
244
245 setName(name) {
246 if (this.nameExist()) {
247 return;
248 }
249
250 this.name = name;
251 }
252 }
253
254 class Man extends Animal {
255 sayName() {
256 console.log('name', this.name);
257 }
258 }
259
260 export default Man;
261`;
262```
263pass it to
264```javascript
265const { createPresentationGenerator } = js2flowchart;
266
267const presentationGenerator = createPresentationGenerator(code);
268const slides = presentationGenerator.buildSlides();//array of SVGs
269```
270
271Result (one of slides):
272
273![](/docs/examples/one-module-presentation/flowchart-image.png)
274
275You can switch slides by prev-next buttons.
276
277[<img src="/docs/examples/one-module-presentation/slides.gif" width="500">](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/one-module-presentation/index.html)
278
279See the example running [here](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/one-module-presentation/index.html) or check out complete source code [of it](/docs/examples/one-module-presentation/index.html).
280
281
282#### Defined colors theme
283
284You can apply different themes to your ```svgRender``` instance. Simply calling e.g. ```svgRender.applyLightTheme()``` to apply light scheme.
285
286There are next predefined color schemes:
287- DEFAULT: ```applyDefaultTheme```
288- BLACK_AND_WHITE: ```applyBlackAndWhiteTheme```
289- BLURRED: ```applyBlurredTheme```
290- LIGHT: ```applyLightTheme```
291
292Let's simple code sample of ```switch``` statement from Mozzila Web Docs.
293```javascript
294const code = `
295 function switchSampleFromMDN() {
296 const foo = 0;
297
298 switch (foo) {
299 case -1:
300 console.log('negative 1');
301 break;
302 case 0:
303 console.log(0);
304 case 1:
305 console.log(1);
306 return 1;
307 default:
308 console.log('default');
309 }
310 }
311`;
312```
313and apply scheme to render.
314
315```javascript
316const {createSVGRender, convertCodeToFlowTree} = js2flowchart;
317
318const flowTree = convertCodeToFlowTree(code),
319 svgRender = createSVGRender();
320
321//applying another theme for render
322svgRender.applyLightTheme();
323
324const svg = svgRender.buildShapesTree(flowTree).print();
325```
326
327Result:
328
329![](/docs/examples/defined-color-theme/flowchart-image.png)
330
331See the example running [here](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/defined-color-theme/index.html) or check out complete source code [of it](/docs/examples/defined-color-theme/index.html).
332
333#### Custom colors theme
334
335Well, but what if you would like to have different colors? Sure, below is an example of Light theme colors but created manually.
336```javascript
337svgRender.applyColorBasedTheme({
338 strokeColor: '#555',
339 defaultFillColor: '#fff',
340 textColor: '#333',
341 arrowFillColor: '#444',
342 rectangleFillColor: '#bbdefb',
343 rectangleDotFillColor: '#ede7f6',
344 functionFillColor: '#c8e6c9',
345 rootCircleFillColor: '#fff9c4',
346 loopFillColor: '#d1c4e9',
347 conditionFillColor: '#e1bee7',
348 destructedNodeFillColor: '#ffecb3',
349 classFillColor: '#b2dfdb',
350 debuggerFillColor: '#ffcdd2',
351 exportFillColor: '#b3e5fc',
352 throwFillColor: '#ffccbc',
353 tryFillColor: '#FFE082',
354 objectFillColor: '#d1c4e9',
355 callFillColor: '#dcedc8',
356 debugModeFillColor: '#666'
357});
358```
359
360#### Custom styles
361
362What if you need different styles, not only colors? Here it's ```svgRender.applyTheme({})```. You can apply styles above of current theme, overriding only that behaviour you need.
363Let's take an example with Return statement.
364```javascript
365svgRender.applyTheme({
366 ReturnStatement: {
367 fillColor: 'red',
368 roundBorder: 10
369 }
370});
371```
372
373Please check definition of [```DefaultBaseTheme```](/src/render/svg/appearance/themes/DefaultBaseTheme.js) to see all possible shapes names and properties.
374
375
376#### Shapes tree editor
377
378There is sub-module for modifying shapes tree called 'ShapesTreeEditor'.
379It provides next interfaces:
380- ```findShape```
381- ```applyShapeStyles```
382- ```blur```
383- ```focus```
384- ```blurShapeBranch```
385- ```focusShapeBranch```
386- ```print```
387
388Let's learn its usage on an example as well. Below is the code with some 'devMode hooks'.
389```javascript
390const code = `
391const doStuff = (stuff) => {
392 if (stuff) {
393 if (devFlag) {
394 log('perf start');
395 doRecursion();
396 log('perf end');
397
398 return;
399 }
400
401 doRecursion();
402 end();
403 } else {
404 throw new Error('No stuff!');
405 }
406
407 return null;
408};
409`;
410```
411
412what we want here is 'blur' that dev-branch condition, because it interferes code readability.
413
414```javascript
415const {
416 convertCodeToFlowTree,
417 createSVGRender,
418 createShapesTreeEditor
419} = js2flowchart;
420
421const flowTree = convertCodeToFlowTree(code),
422 svgRender = createSVGRender();
423 shapesTree = svgRender.buildShapesTree(flowTree);
424
425const shapesTreeEditor = createShapesTreeEditor(shapesTree);
426
427shapesTreeEditor.blurShapeBranch(
428 (shape) => shape.getName() === '(devFlag)'
429);
430
431const svg = shapesTreeEditor.print();
432```
433
434Result:
435
436![](/docs/examples/blur-shape-branch/flowchart-image.png)
437
438See the example running [here](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/blur-shape-branch/index.html) or check out complete source code [of it](/docs/examples/blur-shape-branch/index.html).
439
440
441#### Flow tree modifier
442
443There is sub-module for modifying flow tree called 'FlowTreeModifier' which allows you to apply modifiers defined separately to your existing flow tree.
444Let's take simple use-case: you want to change 'names'(titles) on tree-nodes, here it is, just define modifier for that. But, actually, there are some behaviours where we already know we need to modify flow tree.
445
446Let's have a look at ES5 Array iterators, like ```forEach```, ```map``` and so on. We all know they behave like a loop, right? Let's treat them as a 'loop' then.
447
448```javascript
449const code = `
450function print(list) {
451 const newList = list.map(i => {
452 return i + 1;
453 });
454
455 newList.forEach(i => {
456 console.debug('iteration start');
457 console.log(i);
458 console.debug('iteration end');
459 });
460}
461`;
462```
463
464
465```javascript
466const {
467 createFlowTreeBuilder,
468 createFlowTreeModifier,
469 convertFlowTreeToSvg,
470 MODIFIER_PRESETS
471} = js2flowchart;
472
473const flowTreeBuilder = createFlowTreeBuilder(),
474 flowTree = flowTreeBuilder.build(code);
475
476const flowTreeModifier = createFlowTreeModifier();
477
478flowTreeModifier.setModifier(MODIFIER_PRESETS.es5ArrayIterators);
479flowTreeModifier.applyToFlowTree(flowTree);
480
481const svg = convertFlowTreeToSvg(flowTree);
482```
483
484Result:
485
486As you can see, both iterators handled as a loop. And ```forEach``` omit function-callback as well.
487
488![](/docs/examples/defined-modifier/flowchart-image.png)
489
490See the example running [here](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/defined-modifier/index.html) or check out complete source code [of it](/docs/examples/defined-modifier/index.html).
491
492
493There is one more defined modifier for node destruction. It takes a block you specified and destruct it to on block.
494```javascript
495flowTreeModifier.destructNodeTree((node) => node.name.indexOf('.forEach') !== -1, 'and print list...');
496```
497What if you want **custom modifier**?
498```javascript
499flowTreeModifier.registerNewModifier((node)=> node.name.includes('hello'), {
500 name: 'world'
501});
502```
503
504#### Debug rendering
505What if you want to select a shape for applying special styles and want some unique id selector? Just pass ```debug``` flag to ```print```;
506
507```javascript
508 const {
509 convertCodeToFlowTree,
510 createSVGRender,
511 createShapesTreeEditor
512 } = js2flowchart;
513
514 const svgRender = createSVGRender();
515
516 const shapesTree = svgRender.buildShapesTree(convertCodeToFlowTree(code));
517 const shapesTreeEditor = createShapesTreeEditor(shapesTree);
518
519 shapesTreeEditor.applyShapeStyles(
520 shape => shape.getNodePathId() === 'NODE-ID:|THIS.NAME=N|TCCP-', {
521 fillColor: '#90caf9'
522 });
523
524 const svg = shapesTreeEditor.print({debug: true});
525```
526
527Result:
528
529![](/docs/examples/debug-rendering/flowchart-image.png)
530
531See the example running [here](https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/examples/debug-rendering/index.html) or check out complete source code [of it](/docs/examples/debug-rendering/index.html).
532
533### Under the hood
534Main stages:
535- get AST from code, [Babylon](https://github.com/babel/babel/tree/master/packages/babylon) parser is used (develops by Babel team)
536- convert AST to FlowTree, remove and combing nodes ([FlowTreeBuilder](src/builder/FlowTreeBuilder.js))
537 - apply modifiers ([FlowTreeModifier](src/builder/FlowTreeModifier.js))
538- create SVG objects based on FlowTree ([SVGRender](src/render/svg/SVGRender.js))
539 - apply ShapesTreeEditor
540 - apply theme ([see themes](src/render/svg/appearance/themes))
541- print SVG objects to XML string
542
543
544### Things planned TODO
545- Full CLI support
546- JSX support
547- Flow support
548- TypeScript support
549- Multi files support
550- Webstorm plugin
551- Chrome extension for dev-tools
552
553### Contributing
554Feel free to file an issue if it doesn't work for your code sample (please add 'breaking' lines of code if it's possible to identify) or for any other things you think can be improved.
555Highly appreciated if you can join and help with any TODOs above. Thanks.
556
557### License
558MIT license
559
560### Version
561First shoot! Take it easy (check version number above in NPM badge)
562