├── .github
└── FUNDING.yml
├── .gitignore
├── README.md
├── __snapshots__
└── parser.test.js.snap
├── cli.js
├── docs-source
├── .gitignore
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── attina.js
│ ├── editor.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── serviceWorker.js
└── yarn.lock
├── docs
├── asset-manifest.json
├── favicon.ico
├── index.html
├── manifest.json
├── precache-manifest.0f46b7a65bc3f0cdb2f6bf6e214ef341.js
├── service-worker.js
└── static
│ ├── css
│ ├── main.4fd1df1e.chunk.css
│ └── main.4fd1df1e.chunk.css.map
│ └── js
│ ├── 1.b775380a.chunk.js
│ ├── 1.b775380a.chunk.js.map
│ ├── main.96947aa3.chunk.js
│ ├── main.96947aa3.chunk.js.map
│ ├── runtime~main.367d1e08.js
│ └── runtime~main.367d1e08.js.map
├── example.1.js
├── example.js
├── index.js
├── index.test.js
├── mermaid-diagram-example.svg
├── package-lock.json
├── package.json
├── parser.test.js
├── transform.js
└── yarn.lock
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [crubier] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | # patreon: # Replace with a single Patreon username
5 | # open_collective: # Replace with a single Open Collective username
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # otechie: # Replace with a single Otechie username
12 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # code-to-graph
2 |
3 | Transforms code (JS) into graphs (graphviz, mermaid flowchart, ...)
4 |
5 | ## Interactive Demo
6 |
7 | https://crubier.github.io/code-to-graph/
8 |
9 | [A bigger example](https://crubier.github.io/code-to-graph/?code=aW1wb3J0IHsgdG90byB9IGZyb20gInR1dHUiOwoKZnVuY3Rpb24gdHJ5Q2F0Y2hFeGFtcGxlKGFyZ3MpIHsKICBmdW5jdGlvbiBmKCkgewogICAgY29uc3QgZCA9IGFzeW5jICgpID0-IHsKICAgICAgYXdhaXQgNTsKICAgICAgcmV0dXJuIHRvdG87CiAgICB9OwogICAgY29uc3QgZmYgPSA0NTsKICAgIHRyeSB7CiAgICAgIGxldCBhID0gNTsKICAgICAgYSA9IGEgKyAxOwogICAgICBpZiAoYSA-IDMpIHsKICAgICAgICB0aHJvdyBuZXcgRXJyb3IoIm5vdCBub3JtYWwiKTsKICAgICAgfQogICAgfSBjYXRjaCAoZSkgewogICAgICBjb25zb2xlLmxvZygiaW4gdGhlIGNhdGNoIik7CiAgICAgIGlmICh2KSByZXR1cm4gNTsKICAgICAgaWYgKGZ0dykgdGhyb3cgbmV3IEVycm9yKCJSZWFsIGJhZCIpOwogICAgfSBmaW5hbGx5IHsKICAgICAgY29uc29sZS5sb2coImluIHRoZSBmaW5hbGx5Iik7CiAgICAgIGlmICh1KSByZXR1cm4gODsKICAgIH0KICAgIHJldHVybiAxOwogIH0KfQoKZXhwb3J0IGFzeW5jIGZ1bmN0aW9uKiBnZW5lcmF0b3JFeGFtcGxlKGFyZ3MpIHsKICB5aWVsZCAxOwogIHlpZWxkIDU7CiAgaWYgKG15Q29uZCkgewogICAgY29uc3QgYyA9IHlpZWxkIDIxOwogICAgY29uc3QgZCA9IGF3YWl0IGNvY28oYyk7CiAgfSBlbHNlIHsKICAgIHlpZWxkIDI1OwogICAgbGV0IHUgPSB5aWVsZCBmKDQ1ICsgNik7CiAgfQogIGZvciBhd2FpdCAobGV0IGEgb2YgYikgewogICAgY29uc3QgYiA9IGF3YWl0IGYoYSk7CiAgICB5aWVsZCBiICsgYjsKICAgIGZyYW1lRWxlbWVudCgyKTsKICAgIGNvbnN0IHUgPSB5aWVsZCA2OwogIH0KICByZXR1cm4gZDsKfQoKYXN5bmMgZnVuY3Rpb24gY29udHJvbEZsb3dFeGFtcGxlKHgpIHsKICBjb25zdCBhID0gNTsKICBjb25zdCBiID0gYXdhaXQgdG90byg0NCk7CiAgd2hpbGUgKGIgPiAwKSB7CiAgICBpZiAoYikgewogICAgICByZXR1cm4gNTsKICAgIH0gZWxzZSBpZiAoYykgewogICAgICB0aHJvdyBuZXcgRXJyb3IoIk5vdCIpOwogICAgfSBlbHNlIHsKICAgICAgYiA9IDUyOwogICAgfQogIH0KICBkbyB7CiAgICBiID0gYiArIDE7CiAgICBjID0gZihrKTsKICAgIGlmIChrKSB7CiAgICAgIGJyZWFrOwogICAgfQogIH0gd2hpbGUgKGIgPCA0KTsKICBpZiAoaysrID09IDUpIHsKICAgIGNvdWNvdTIgPSAyNDsKICB9CiAgZm9yIChsZXQgdCA9IDA7IHQgPCA1OyB0KyspIHsKICAgIGlmIChsKSB7CiAgICAgIGYoaCk7CiAgICB9IGVsc2UgewogICAgICB3ZWJraXRDYW5jZWxBbmltYXRpb25GcmFtZSgzMik7CiAgICB9CiAgfQogIHJldHVybiBhICogMzsKfQoKY2xhc3MgRXhhbXBsZUNsYXNzIHsKICBteU1ldGhvZCh4KSB7CiAgICBjb25zb2xlLmxvZygibG9sIik7CiAgICByZXR1cm47CiAgfQogIGV4YW1wbGVNZXRob2QoeCkgewogICAgaWYgKG15Q29uZGl0aW9uKSByZXR1cm4gNTsKICAgIHJldHVybiA2OwogIH0KICBwcm9wZXJ0eSA9IDg7CiAgc3RhdGljIHN0YXRpY1Byb3AgPSAxOTsKICByZW5kZXIoKSB7CiAgICByZXR1cm4gKAogICAgICA8ZGl2PgogICAgICAgIDxzcGFuPlRvdG88L3NwYW4-CiAgICAgICAgPHNwYW4-VGl0aTwvc3Bhbj4KICAgICAgPC9kaXY-CiAgICApOwogIH0KfQoKZXhwb3J0IGRlZmF1bHQgdG90bzsK)
10 |
11 | ## CLI Usage
12 |
13 | Call the cli with a js file name, it prints out the Mermaid.js graph definition
14 |
15 | ```bash
16 | yarn global add code-to-graph
17 | code-to-graph example.js
18 | ```
19 |
20 | To see the result visualy, paste it in
21 |
22 | https://mermaidjs.github.io/mermaid-live-editor
23 |
24 | Or you can use it on gitlab using the ```mermaid language in comments, descriptions or markdown files.
25 |
26 | ## Explanation
27 |
28 | Turns this:
29 |
30 | ```javascript
31 | x => {
32 | const a = f(x);
33 | if (x === 0) {
34 | let a = null;
35 | throw new Error("Nooes");
36 | } else {
37 | const c = 8;
38 | return 4;
39 | }
40 | };
41 | ```
42 |
43 | Into this:
44 |
45 | ```mermaid
46 | graph TD
47 | statementfroml1c6tol1c19("const a = f(x);")
48 | statementfroml1c19tol1c90{"x === 0"}
49 | statementfroml1c29tol1c40("let a = null;")
50 | statementfroml1c41tol1c65>"throw new Error('Nooes');"]
51 | style statementfroml1c41tol1c65 fill:#FF9999
52 | statementfroml1c71tol1c81("const c = 8;")
53 | statementfroml1c81tol1c89>"return 4;"]
54 | style statementfroml1c81tol1c89 fill:#99FF99
55 | statementfroml1c19tol1c90 -- true --> statementfroml1c29tol1c40
56 | statementfroml1c19tol1c90 -- false --> statementfroml1c71tol1c81
57 | statementfroml1c29tol1c40 --> statementfroml1c41tol1c65
58 | statementfroml1c71tol1c81 --> statementfroml1c81tol1c89
59 | statementfroml1c6tol1c19 --> statementfroml1c19tol1c90
60 | ```
61 |
62 | Whichs renders into this:
63 |
64 | 
65 |
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require("fs");
4 | const opn = require("opn");
5 | const { transformJsStringToMermaidString } = require(".");
6 |
7 | const [, , ...args] = process.argv;
8 |
9 | // console.log(`Hello world ${args}`);
10 |
11 | function main(args) {
12 | const string = fs.readFileSync(args[0], { encoding: "utf8" });
13 | const result = transformJsStringToMermaidString(string);
14 | console.log("\n\n");
15 | console.log(result);
16 | console.log("\n\n");
17 | try {
18 | const base64Result = Buffer.from(result).toString("base64");
19 | opn(
20 | `https://mermaidjs.github.io/mermaid-live-editor/#/edit/${base64Result}`,
21 | { wait: false }
22 | );
23 | } catch (e) {
24 | return;
25 | }
26 | }
27 |
28 | main(args);
29 |
--------------------------------------------------------------------------------
/docs-source/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/docs-source/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/docs-source/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code-to-graph",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "https://crubier.github.io/code-to-graph",
6 | "dependencies": {
7 | "@fortawesome/fontawesome-svg-core": "^1.2.12",
8 | "@fortawesome/free-solid-svg-icons": "^5.6.3",
9 | "@fortawesome/react-fontawesome": "^0.1.3",
10 | "attina": "^1.0.1",
11 | "brace": "^0.11.1",
12 | "code-to-graph": "1.0.15",
13 | "copy-to-clipboard": "^3.0.8",
14 | "file-saver": "^2.0.0",
15 | "js-base64": "^2.5.0",
16 | "lodash": "^4.17.11",
17 | "mermaid": "^8.0.0",
18 | "query-string": "^6.2.0",
19 | "react": "^16.7.0",
20 | "react-ace": "^6.3.2",
21 | "react-dom": "^16.7.0",
22 | "react-dropzone": "^8.0.3",
23 | "react-github-corner": "^2.3.0",
24 | "react-mermaid": "^0.1.3",
25 | "react-router": "^4.3.1",
26 | "react-router-dom": "^4.3.1",
27 | "react-scripts": "2.1.3"
28 | },
29 | "scripts": {
30 | "start": "react-scripts start",
31 | "build": "react-scripts build",
32 | "docs": "yarn build && rm -rf ../docs && mv ./build ../docs",
33 | "test": "react-scripts test",
34 | "eject": "react-scripts eject"
35 | },
36 | "eslintConfig": {
37 | "extends": "react-app"
38 | },
39 | "browserslist": [
40 | ">0.2%",
41 | "not dead",
42 | "not ie <= 11",
43 | "not op_mini all"
44 | ]
45 | }
--------------------------------------------------------------------------------
/docs-source/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crubier/code-to-graph/b261d8233de9569ab2fc74f9d1ee520f7546b1dd/docs-source/public/favicon.ico
--------------------------------------------------------------------------------
/docs-source/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
23 | Code to Graph
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs-source/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Code to Graph",
3 | "name": "Code to Graph",
4 | "icons": [{
5 | "src": "favicon.ico",
6 | "sizes": "64x64 32x32 24x24 16x16",
7 | "type": "image/x-icon"
8 | }],
9 | "start_url": ".",
10 | "display": "standalone",
11 | "theme_color": "#000000",
12 | "background_color": "#ffffff"
13 | }
--------------------------------------------------------------------------------
/docs-source/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | }
9 |
10 | .App-header {
11 | background-color: #282c34;
12 | min-height: 100vh;
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: calc(10px + 2vmin);
18 | color: white;
19 | }
20 |
21 | .App-link {
22 | color: #61dafb;
23 | }
24 |
25 | @keyframes App-logo-spin {
26 | from {
27 | transform: rotate(0deg);
28 | }
29 | to {
30 | transform: rotate(360deg);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/docs-source/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | // import logo from "./logo.svg";
3 | import "./App.css";
4 |
5 | import Editor from "./editor";
6 | import GithubCorner from "react-github-corner";
7 | import { BrowserRouter as Router, Route } from "react-router-dom";
8 |
9 | class App extends Component {
10 | render() {
11 | return (
12 |
13 |
18 |
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/docs-source/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/docs-source/src/attina.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { mermaidAPI } from "mermaid";
3 | import FileSaver from "file-saver";
4 |
5 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6 | import { faDownload } from "@fortawesome/free-solid-svg-icons";
7 |
8 | // import debounce from "lodash/fp/debounce";
9 | // mermaidAPI.initialize({
10 | // startOnLoad: true,
11 | // logLevel: 1
12 | // });
13 |
14 | function getDiagram(title, input, callback) {
15 | try {
16 | mermaidAPI.initialize({
17 | startOnLoad: true
18 | });
19 |
20 | mermaidAPI.parseError = function(err, hash) {
21 | // console.log("parseError");
22 | // console.log(err.messsage);
23 | };
24 | // console.log("Rendering");
25 | // console.log(input);
26 | // mermaidAPI.render(title, "graph TB;Loading;", diagram => {
27 | // console.log("Rendered");
28 | // setTimeout(
29 | // () =>
30 | // mermaidAPI.render(title, input, diagram => {
31 | // console.log("Rendered");
32 | // console.log(diagram);
33 | // callback(diagram);
34 | // }),
35 | // 200
36 | // );
37 | // });
38 |
39 | mermaidAPI.render(title, input, diagram => {
40 | // console.log("Rendered");
41 | // console.log(diagram);
42 | callback({ diagram, input });
43 | });
44 | } catch (e) {
45 | console.log("Failed to generate diagram");
46 | console.log(e);
47 | }
48 | }
49 |
50 | class Attina extends React.Component {
51 | constructor(props) {
52 | super(props);
53 |
54 | this.state = {
55 | diagram: {
56 | __html: ""
57 | }
58 | };
59 | }
60 |
61 | componentDidMount() {
62 | getDiagram(this.props.title, this.props.diagram, ({ diagram, input }) => {
63 | this.setState({ diagram: { __html: diagram }, input });
64 | });
65 | }
66 |
67 | shouldComponentUpdate(nextProps, nextState) {
68 | if (nextProps.diagram !== this.props.diagram) {
69 | getDiagram(this.props.title, nextProps.diagram, ({ diagram, input }) => {
70 | this.setState({ diagram: { __html: diagram }, input });
71 | });
72 |
73 | return false;
74 | }
75 | if (nextState.diagram !== this.state.diagram) {
76 | return true;
77 | }
78 | return false;
79 | }
80 |
81 | render() {
82 | return (
83 |
84 |
118 |
128 |
129 | );
130 | }
131 | }
132 |
133 | Attina.defaultProps = {
134 | title: "diagram",
135 | frameBorder: 0
136 | };
137 |
138 | export default Attina;
139 |
--------------------------------------------------------------------------------
/docs-source/src/editor.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | // import logo from "./logo.svg";
3 | import "./App.css";
4 | import Attina from "./attina";
5 | import debounce from "lodash/fp/debounce";
6 | import { transformJsStringToMermaidString } from "code-to-graph";
7 | import copy from "copy-to-clipboard";
8 |
9 | import brace from "brace";
10 | import AceEditor from "react-ace";
11 | import Dropzone from "react-dropzone";
12 |
13 | import "brace/mode/javascript";
14 | import "brace/mode/text";
15 | import "brace/snippets/javascript";
16 | import "brace/theme/github";
17 | import "brace/theme/monokai";
18 | import "brace/ext/language_tools";
19 | import "brace/ext/searchbox";
20 |
21 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
22 | import {
23 | faSpinner,
24 | faPlay,
25 | faShareSquare,
26 | faUpload
27 | } from "@fortawesome/free-solid-svg-icons";
28 |
29 | import { Base64 } from "js-base64";
30 |
31 | import queryString from "query-string";
32 |
33 | const getCodeFromLocation = ({ location }) => {
34 | try {
35 | // console.log("code");
36 | const search = location.search;
37 | // console.log(search);
38 | const params = queryString.parse(search);
39 | // console.log(params);
40 | const codeBase64 = params.code;
41 | // console.log(codeBase64);
42 | const code = Base64.decode(codeBase64);
43 | // console.log(code);
44 | return { code: code };
45 | } catch (e) {
46 | console.log("Using default code");
47 | return {
48 | code: `const myFunction = (x) => {
49 | if(x>0){
50 | return "ok"
51 | } else {
52 | throw "not ok"
53 | }
54 | }`
55 | };
56 | }
57 | };
58 |
59 | class App extends Component {
60 | constructor(props) {
61 | super(props);
62 | this.state = {
63 | source: `graph TB; Init;`,
64 | error: null,
65 | status: "init"
66 | };
67 |
68 | this.handleChange = this.handleChange.bind(this);
69 | this.handleSubmitDirect = this.handleSubmitDirect.bind(this);
70 | this.handleSubmit = debounce(200, this.handleSubmitDirect).bind(this);
71 | }
72 |
73 | onDrop = (acceptedFiles, rejectedFiles) => {
74 | // console.log("drop", acceptedFiles, rejectedFiles);
75 |
76 | acceptedFiles.forEach(file => {
77 | const reader = new FileReader();
78 | reader.onload = () => {
79 | const fileAsBinaryString = reader.result;
80 | this.handleChange(fileAsBinaryString);
81 | };
82 | reader.onabort = () => console.log("file reading was aborted");
83 | reader.onerror = () => console.log("file reading has failed");
84 |
85 | reader.readAsBinaryString(file);
86 | });
87 | };
88 |
89 | handleChange(newValue) {
90 | const base64Result = Base64.encodeURI(newValue);
91 | this.props.history.push(`./?code=${base64Result}`);
92 | this.handleSubmit();
93 | }
94 |
95 | handleSubmitDirect() {
96 | setTimeout(() => {
97 | const { code } = getCodeFromLocation({ location: this.props.location });
98 | this.setState(
99 | {
100 | source: `graph TB; Loading;`,
101 | status: "loading"
102 | },
103 | () => {
104 | setTimeout(() => {
105 | try {
106 | const source = transformJsStringToMermaidString(code);
107 | this.setState({
108 | source: source,
109 | status: "loaded",
110 | error: null
111 | });
112 | } catch (error) {
113 | this.setState({
114 | status: "error",
115 | error: error
116 | });
117 | }
118 | }, 0);
119 | }
120 | );
121 | }, 0);
122 | }
123 |
124 | componentDidMount() {
125 | try {
126 | const { code } = getCodeFromLocation({ location: this.props.location });
127 | this.handleChange(code);
128 | } catch (error) {
129 | this.setState({
130 | status: "error",
131 | error: error
132 | });
133 | }
134 | }
135 |
136 | render() {
137 | const { code } = getCodeFromLocation({ location: this.props.location });
138 | return (
139 |
140 | {({ getRootProps, getInputProps, isDragActive }) => {
141 | const inputProps = getInputProps();
142 | // console.log(inputProps);
143 | return (
144 |
156 | {isDragActive ? (
157 |
173 | Drop file anywhere
174 |
175 | ) : null}
176 |
188 |
210 |
229 |
230 |
234 | {" "}
235 | Drop code files anywhere or
236 |
237 | {" "}
238 |
256 |
257 |
258 |
259 |
284 | {this.state.error === null || this.state.error === undefined ? (
285 |
295 |
299 |
332 |
363 |
364 | ) : (
365 |
408 | )}
409 |
410 | );
411 | }}
412 |
413 | );
414 | }
415 | }
416 |
417 | export default App;
418 |
--------------------------------------------------------------------------------
/docs-source/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/docs-source/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: http://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/docs-source/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/docs-source/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read http://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/docs/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "main.css": "/code-to-graph/static/css/main.4fd1df1e.chunk.css",
3 | "main.js": "/code-to-graph/static/js/main.96947aa3.chunk.js",
4 | "main.js.map": "/code-to-graph/static/js/main.96947aa3.chunk.js.map",
5 | "static/js/1.b775380a.chunk.js": "/code-to-graph/static/js/1.b775380a.chunk.js",
6 | "static/js/1.b775380a.chunk.js.map": "/code-to-graph/static/js/1.b775380a.chunk.js.map",
7 | "runtime~main.js": "/code-to-graph/static/js/runtime~main.367d1e08.js",
8 | "runtime~main.js.map": "/code-to-graph/static/js/runtime~main.367d1e08.js.map",
9 | "static/css/main.4fd1df1e.chunk.css.map": "/code-to-graph/static/css/main.4fd1df1e.chunk.css.map",
10 | "index.html": "/code-to-graph/index.html",
11 | "precache-manifest.0f46b7a65bc3f0cdb2f6bf6e214ef341.js": "/code-to-graph/precache-manifest.0f46b7a65bc3f0cdb2f6bf6e214ef341.js",
12 | "service-worker.js": "/code-to-graph/service-worker.js"
13 | }
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crubier/code-to-graph/b261d8233de9569ab2fc74f9d1ee520f7546b1dd/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | Code to Graph
--------------------------------------------------------------------------------
/docs/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Code to Graph",
3 | "name": "Code to Graph",
4 | "icons": [{
5 | "src": "favicon.ico",
6 | "sizes": "64x64 32x32 24x24 16x16",
7 | "type": "image/x-icon"
8 | }],
9 | "start_url": ".",
10 | "display": "standalone",
11 | "theme_color": "#000000",
12 | "background_color": "#ffffff"
13 | }
--------------------------------------------------------------------------------
/docs/precache-manifest.0f46b7a65bc3f0cdb2f6bf6e214ef341.js:
--------------------------------------------------------------------------------
1 | self.__precacheManifest = [
2 | {
3 | "revision": "367d1e08377a7d6deb73",
4 | "url": "/code-to-graph/static/js/runtime~main.367d1e08.js"
5 | },
6 | {
7 | "revision": "96947aa3bc5e99ad6822",
8 | "url": "/code-to-graph/static/js/main.96947aa3.chunk.js"
9 | },
10 | {
11 | "revision": "b775380acf87012bed38",
12 | "url": "/code-to-graph/static/js/1.b775380a.chunk.js"
13 | },
14 | {
15 | "revision": "96947aa3bc5e99ad6822",
16 | "url": "/code-to-graph/static/css/main.4fd1df1e.chunk.css"
17 | },
18 | {
19 | "revision": "027774cdcf6548e2b7cd9cbfae5f7514",
20 | "url": "/code-to-graph/index.html"
21 | }
22 | ];
--------------------------------------------------------------------------------
/docs/service-worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Welcome to your Workbox-powered service worker!
3 | *
4 | * You'll need to register this file in your web app and you should
5 | * disable HTTP caching for this file too.
6 | * See https://goo.gl/nhQhGp
7 | *
8 | * The rest of the code is auto-generated. Please don't update this file
9 | * directly; instead, make changes to your Workbox build configuration
10 | * and re-run your build process.
11 | * See https://goo.gl/2aRDsh
12 | */
13 |
14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
15 |
16 | importScripts(
17 | "/code-to-graph/precache-manifest.0f46b7a65bc3f0cdb2f6bf6e214ef341.js"
18 | );
19 |
20 | workbox.clientsClaim();
21 |
22 | /**
23 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to
24 | * requests for URLs in the manifest.
25 | * See https://goo.gl/S9QRab
26 | */
27 | self.__precacheManifest = [].concat(self.__precacheManifest || []);
28 | workbox.precaching.suppressWarnings();
29 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
30 |
31 | workbox.routing.registerNavigationRoute("/code-to-graph/index.html", {
32 |
33 | blacklist: [/^\/_/,/\/[^\/]+\.[^\/]+$/],
34 | });
35 |
--------------------------------------------------------------------------------
/docs/static/css/main.4fd1df1e.chunk.css:
--------------------------------------------------------------------------------
1 | body{margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.App{text-align:center}.App-logo{-webkit-animation:App-logo-spin 20s linear infinite;animation:App-logo-spin 20s linear infinite;height:40vmin}.App-header{background-color:#282c34;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:calc(10px + 2vmin);color:#fff}.App-link{color:#61dafb}@-webkit-keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}
2 | /*# sourceMappingURL=main.4fd1df1e.chunk.css.map */
--------------------------------------------------------------------------------
/docs/static/css/main.4fd1df1e.chunk.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["/Users/vincent/Code/code-to-graph/docs-source/src/index.css","/Users/vincent/Code/code-to-graph/docs-source/src/App.css"],"names":[],"mappings":"AAAA,KACE,SAAU,AACV,UAAW,AACX,oIAEa,AACb,mCAAoC,AACpC,iCAAmC,CACpC,AAED,KACE,uEACY,CACb,ACbD,KACE,iBAAmB,CACpB,AAED,UACE,oDAA6C,AAA7C,4CAA6C,AAC7C,aAAe,CAChB,AAED,YACE,yBAA0B,AAC1B,iBAAkB,AAClB,aAAc,AACd,sBAAuB,AACvB,mBAAoB,AACpB,uBAAwB,AACxB,6BAA8B,AAC9B,UAAa,CACd,AAED,UACE,aAAe,CAChB,AAED,iCACE,GACE,+BAAwB,AAAxB,sBAAwB,CACzB,AACD,GACE,gCAA0B,AAA1B,uBAA0B,CAC3B,CACF,AAPD,yBACE,GACE,+BAAwB,AAAxB,sBAAwB,CACzB,AACD,GACE,gCAA0B,AAA1B,uBAA0B,CAC3B,CACF","file":"main.4fd1df1e.chunk.css","sourcesContent":["body {\n margin: 0;\n padding: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n",".App {\n text-align: center;\n}\n\n.App-logo {\n animation: App-logo-spin infinite 20s linear;\n height: 40vmin;\n}\n\n.App-header {\n background-color: #282c34;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"]}
--------------------------------------------------------------------------------
/docs/static/js/main.96947aa3.chunk.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{190:function(e,t,n){e.exports=n(455)},195:function(e,t,n){},455:function(e,t,n){"use strict";n.r(t);var a=n(1),o=n.n(a),r=n(179),i=n.n(r),l=(n(195),n(24)),s=n(25),c=n(27),d=n(26),u=n(28),h=(n(93),n(14)),m=n(189),f=n(54),p=n(180),g=n.n(p),b=n(21),x=n(22);function y(e,t,n){try{f.mermaidAPI.initialize({startOnLoad:!0}),f.mermaidAPI.parseError=function(e,t){},f.mermaidAPI.render(e,t,function(e){n({diagram:e,input:t})})}catch(a){console.log("Failed to generate diagram"),console.log(a)}}var v=function(e){function t(e){var n;return Object(l.a)(this,t),(n=Object(c.a)(this,Object(d.a)(t).call(this,e))).state={diagram:{__html:""}},n}return Object(u.a)(t,e),Object(s.a)(t,[{key:"componentDidMount",value:function(){var e=this;y(this.props.title,this.props.diagram,function(t){var n=t.diagram,a=t.input;e.setState({diagram:{__html:n},input:a})})}},{key:"shouldComponentUpdate",value:function(e,t){var n=this;return e.diagram!==this.props.diagram?(y(this.props.title,e.diagram,function(e){var t=e.diagram,a=e.input;n.setState({diagram:{__html:t},input:a})}),!1):t.diagram!==this.state.diagram}},{key:"render",value:function(){return o.a.createElement(o.a.Fragment,null,o.a.createElement("button",{onClick:function(){var e=new Blob([document.getElementsByClassName("mermaid-code-to-graph")[0].innerHTML],{type:"image/svg+xml;charset=utf-8"});g.a.saveAs(e,"graph.svg")},style:{color:"white",borderRadius:"0",border:"none",outline:"none",backgroundColor:"#FF44FF",flexBasis:"30px",flexShrink:0,flexGrow:1,width:"100%",minHeight:"30px",zIndex:1}},o.a.createElement(b.a,{icon:x.a})," "," "," ",(this.state.status,"Download SVG File")),o.a.createElement("div",{dangerouslySetInnerHTML:this.state.diagram,className:"mermaid-code-to-graph",style:Object(m.a)({display:"flex",alignItems:"center",justifyContent:"center"},this.props.style)}))}}]),t}(o.a.Component);v.defaultProps={title:"diagram",frameBorder:0};var w=v,S=n(181),k=n.n(S),E=n(182),j=n(183),O=n.n(j),C=(n(89),n(55)),F=n.n(C),B=n(188),D=(n(443),n(445),n(446),n(447),n(448),n(449),n(450),n(91)),I=n(185),G=n.n(I),z=function(e){var t=e.location;try{var n=t.search,a=G.a.parse(n).code;return{code:D.Base64.decode(a)}}catch(o){return console.log("Using default code"),{code:'const myFunction = (x) => {\n if(x>0){\n return "ok"\n } else {\n throw "not ok"\n }\n}'}}},A=function(e){function t(e){var n;return Object(l.a)(this,t),(n=Object(c.a)(this,Object(d.a)(t).call(this,e))).onDrop=function(e,t){e.forEach(function(e){var t=new FileReader;t.onload=function(){var e=t.result;n.handleChange(e)},t.onabort=function(){return console.log("file reading was aborted")},t.onerror=function(){return console.log("file reading has failed")},t.readAsBinaryString(e)})},n.state={source:"graph TB; Init;",error:null,status:"init"},n.handleChange=n.handleChange.bind(Object(h.a)(Object(h.a)(n))),n.handleSubmitDirect=n.handleSubmitDirect.bind(Object(h.a)(Object(h.a)(n))),n.handleSubmit=k()(200,n.handleSubmitDirect).bind(Object(h.a)(Object(h.a)(n))),n}return Object(u.a)(t,e),Object(s.a)(t,[{key:"handleChange",value:function(e){var t=D.Base64.encodeURI(e);this.props.history.push("./?code=".concat(t)),this.handleSubmit()}},{key:"handleSubmitDirect",value:function(){var e=this;setTimeout(function(){var t=z({location:e.props.location}).code;e.setState({source:"graph TB; Loading;",status:"loading"},function(){setTimeout(function(){try{var n=Object(E.transformJsStringToMermaidString)(t);e.setState({source:n,status:"loaded",error:null})}catch(a){e.setState({status:"error",error:a})}},0)})},0)}},{key:"componentDidMount",value:function(){try{var e=z({location:this.props.location}).code;this.handleChange(e)}catch(t){this.setState({status:"error",error:t})}}},{key:"render",value:function(){var e=this,t=z({location:this.props.location}).code;return o.a.createElement(B.a,{onDrop:this.onDrop},function(n){var a=n.getRootProps,r=n.getInputProps,i=n.isDragActive,l=r();return o.a.createElement("div",Object.assign({},a(),{onClick:void 0,style:{display:"flex",flexDirection:"row",height:"100vh",alignItems:"stretch",flexWrap:"wrap"}}),i?o.a.createElement("div",{style:{position:"absolute",color:"white",fontSize:"2em",display:"flex",alignItems:"center",justifyContent:"center",left:0,right:0,top:0,bottom:0,backgroundColor:"rgba(0,0,0,0.8)",zIndex:99999}},"Drop file anywhere"):null,o.a.createElement("div",{style:{flexBasis:"50%",flexShrink:1,flexGrow:1,height:"100%",display:"flex",flexDirection:"column",alignItems:"stretch",justifyContent:"stretch"}},o.a.createElement(F.a,{mode:"javascript",theme:"monokai",onChange:e.handleChange,name:"code-ace-editor",editorProps:{$blockScrolling:!0},value:t,style:{flexBasis:"100vh",flexShrink:1,flexGrow:1,width:"100%"},setOptions:{enableBasicAutocompletion:!0,enableLiveAutocompletion:!0,enableSnippets:!0,showLineNumbers:!0,tabSize:2}}),o.a.createElement("div",{style:{color:"white",borderRadius:"0",border:"none",outline:"none",backgroundColor:"#FF44FF",flexBasis:"30px",flexShrink:0,display:"flex",flexDirection:"row",alignItems:"center",justifyContent:"center",flexGrow:1,width:"100%",minHeight:"30px"}},o.a.createElement("span",{style:{fontSize:"11px",marginRight:"1em"}},o.a.createElement(b.a,{icon:x.e,style:{flexShrink:1,flexGrow:1}})," ","Drop code files anywhere or")," ",o.a.createElement("input",Object.assign({},l,{style:{color:"white",borderRadius:"0",border:"none",outline:"none",backgroundColor:"#FF44FF",flexBasis:"fit-content",flexShrink:1,flexGrow:0,display:"flex",flexDirection:"row",alignItems:"center",justifyContent:"center"}})))),o.a.createElement("button",{onClick:e.handleSubmitDirect,style:{position:"relative",fontWeight:600,outline:"none",color:"white",fontSize:"1.2em",borderRadius:"100%",border:"none",backgroundColor:"#FF44FF",width:60,height:60,marginLeft:-30,marginRight:-30,alignSelf:"center",zIndex:9999}},"loading"!==e.state.status?o.a.createElement(b.a,{icon:x.b}):o.a.createElement(b.a,{icon:x.d,spin:!0})),null===e.state.error||void 0===e.state.error?o.a.createElement("div",{style:{flexBasis:"50%",flexShrink:1,flexGrow:0,height:"100%",display:"flex",flexDirection:"column"}},o.a.createElement(w,{diagram:e.state.source,style:{flexBasis:"80%",flexShrink:1,flexGrow:1}}),o.a.createElement("button",{onClick:function(){O()("```mermaid\n"+e.state.source+"\n```",{debug:!0,message:"Press #{key} to copy"}),e.setState({status:"copied"})},style:{color:"white",borderRadius:"0",border:"none",outline:"none",backgroundColor:"#FF44FF",flexBasis:"30px",flexShrink:0,flexGrow:1,width:"100%",minHeight:"30px"}},"copied"!==e.state.status?o.a.createElement(o.a.Fragment,null,o.a.createElement(b.a,{icon:x.c})," ","Copy graph code to clipboard"):"Copied!"),o.a.createElement(F.a,{mode:"text",theme:"github",wrapEnabled:!1,readOnly:!0,name:"result-ace-editor",editorProps:{$blockScrolling:!0},value:"```mermaid\n"+e.state.source+"\n```",style:{flexBasis:"20%",flexShrink:0,flexGrow:0,width:"100%",height:"20%"},setOptions:{enableBasicAutocompletion:!1,enableLiveAutocompletion:!1,enableSnippets:!1,showLineNumbers:!1,tabSize:2}})):o.a.createElement("div",{style:{flexBasis:"50%",flexShrink:0,flexGrow:0,height:"100%",display:"flex",flexDirection:"column"}},o.a.createElement(F.a,{mode:"text",theme:"github",readOnly:!0,wrapEnabled:!0,name:"error-ace-editor",editorProps:{$blockScrolling:!0},value:e.state.error.message,style:{flexBasis:"100%",flexShrink:0,flexGrow:0,width:"100%",height:"100%",color:"red"},setOptions:{enableBasicAutocompletion:!1,enableLiveAutocompletion:!1,enableSnippets:!1,showLineNumbers:!1,tabSize:2}})))})}}]),t}(a.Component),L=n(186),P=n.n(L),R=n(457),T=n(458),_=function(e){function t(){return Object(l.a)(this,t),Object(c.a)(this,Object(d.a)(t).apply(this,arguments))}return Object(u.a)(t,e),Object(s.a)(t,[{key:"render",value:function(){return o.a.createElement(o.a.Fragment,null,o.a.createElement(P.a,{href:"https://github.com/crubier/code-to-graph",style:{zIndex:20},svgStyle:{zIndex:20}}),o.a.createElement(R.a,null,o.a.createElement(T.a,{component:A})))}}]),t}(a.Component);Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/));i.a.render(o.a.createElement(_,null),document.getElementById("root")),"serviceWorker"in navigator&&navigator.serviceWorker.ready.then(function(e){e.unregister()})},93:function(e,t,n){}},[[190,2,1]]]);
2 | //# sourceMappingURL=main.96947aa3.chunk.js.map
--------------------------------------------------------------------------------
/docs/static/js/main.96947aa3.chunk.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["attina.js","editor.js","App.js","serviceWorker.js","index.js"],"names":["getDiagram","title","input","callback","mermaidAPI","initialize","startOnLoad","parseError","err","hash","render","diagram","e","console","log","Attina","props","_this","Object","classCallCheck","this","possibleConstructorReturn","getPrototypeOf","call","state","__html","_this2","_ref","setState","nextProps","nextState","_this3","_ref2","react_default","a","createElement","Fragment","onClick","blob","Blob","document","getElementsByClassName","innerHTML","type","FileSaver","saveAs","style","color","borderRadius","border","outline","backgroundColor","flexBasis","flexShrink","flexGrow","width","minHeight","zIndex","index_es","icon","faDownload","status","dangerouslySetInnerHTML","className","objectSpread","display","alignItems","justifyContent","React","Component","defaultProps","frameBorder","getCodeFromLocation","location","search","codeBase64","queryString","parse","code","Base64","decode","App","onDrop","acceptedFiles","rejectedFiles","forEach","file","reader","FileReader","onload","fileAsBinaryString","result","handleChange","onabort","onerror","readAsBinaryString","source","error","bind","assertThisInitialized","handleSubmitDirect","handleSubmit","debounce","newValue","base64Result","encodeURI","history","push","concat","setTimeout","transformJsStringToMermaidString","es","getRootProps","getInputProps","isDragActive","inputProps","assign","undefined","flexDirection","height","flexWrap","position","fontSize","left","right","top","bottom","lib_default","mode","theme","onChange","name","editorProps","$blockScrolling","value","setOptions","enableBasicAutocompletion","enableLiveAutocompletion","enableSnippets","showLineNumbers","tabSize","marginRight","faUpload","fontWeight","marginLeft","alignSelf","faPlay","faSpinner","spin","attina","copy","debug","message","faShareSquare","wrapEnabled","readOnly","GithubCorner_default","href","svgStyle","BrowserRouter","Route","component","Editor","Boolean","window","hostname","match","ReactDOM","src_App_0","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.96947aa3.chunk.js","sourcesContent":["import React from \"react\";\nimport { mermaidAPI } from \"mermaid\";\nimport FileSaver from \"file-saver\";\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { faDownload } from \"@fortawesome/free-solid-svg-icons\";\n\n// import debounce from \"lodash/fp/debounce\";\n// mermaidAPI.initialize({\n// startOnLoad: true,\n// logLevel: 1\n// });\n\nfunction getDiagram(title, input, callback) {\n try {\n mermaidAPI.initialize({\n startOnLoad: true\n });\n\n mermaidAPI.parseError = function(err, hash) {\n // console.log(\"parseError\");\n // console.log(err.messsage);\n };\n // console.log(\"Rendering\");\n // console.log(input);\n // mermaidAPI.render(title, \"graph TB;Loading;\", diagram => {\n // console.log(\"Rendered\");\n // setTimeout(\n // () =>\n // mermaidAPI.render(title, input, diagram => {\n // console.log(\"Rendered\");\n // console.log(diagram);\n // callback(diagram);\n // }),\n // 200\n // );\n // });\n\n mermaidAPI.render(title, input, diagram => {\n // console.log(\"Rendered\");\n // console.log(diagram);\n callback({ diagram, input });\n });\n } catch (e) {\n console.log(\"Failed to generate diagram\");\n console.log(e);\n }\n}\n\nclass Attina extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n diagram: {\n __html: \"\"\n }\n };\n }\n\n componentDidMount() {\n getDiagram(this.props.title, this.props.diagram, ({ diagram, input }) => {\n this.setState({ diagram: { __html: diagram }, input });\n });\n }\n\n shouldComponentUpdate(nextProps, nextState) {\n if (nextProps.diagram !== this.props.diagram) {\n getDiagram(this.props.title, nextProps.diagram, ({ diagram, input }) => {\n this.setState({ diagram: { __html: diagram }, input });\n });\n\n return false;\n }\n if (nextState.diagram !== this.state.diagram) {\n return true;\n }\n return false;\n }\n\n render() {\n return (\n \n \n \n \n );\n }\n}\n\nAttina.defaultProps = {\n title: \"diagram\",\n frameBorder: 0\n};\n\nexport default Attina;\n","import React, { Component } from \"react\";\n// import logo from \"./logo.svg\";\nimport \"./App.css\";\nimport Attina from \"./attina\";\nimport debounce from \"lodash/fp/debounce\";\nimport { transformJsStringToMermaidString } from \"code-to-graph\";\nimport copy from \"copy-to-clipboard\";\n\nimport brace from \"brace\";\nimport AceEditor from \"react-ace\";\nimport Dropzone from \"react-dropzone\";\n\nimport \"brace/mode/javascript\";\nimport \"brace/mode/text\";\nimport \"brace/snippets/javascript\";\nimport \"brace/theme/github\";\nimport \"brace/theme/monokai\";\nimport \"brace/ext/language_tools\";\nimport \"brace/ext/searchbox\";\n\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport {\n faSpinner,\n faPlay,\n faShareSquare,\n faUpload\n} from \"@fortawesome/free-solid-svg-icons\";\n\nimport { Base64 } from \"js-base64\";\n\nimport queryString from \"query-string\";\n\nconst getCodeFromLocation = ({ location }) => {\n try {\n // console.log(\"code\");\n const search = location.search;\n // console.log(search);\n const params = queryString.parse(search);\n // console.log(params);\n const codeBase64 = params.code;\n // console.log(codeBase64);\n const code = Base64.decode(codeBase64);\n // console.log(code);\n return { code: code };\n } catch (e) {\n console.log(\"Using default code\");\n return {\n code: `const myFunction = (x) => {\n if(x>0){\n return \"ok\"\n } else {\n throw \"not ok\"\n }\n}`\n };\n }\n};\n\nclass App extends Component {\n constructor(props) {\n super(props);\n this.state = {\n source: `graph TB; Init;`,\n error: null,\n status: \"init\"\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleSubmitDirect = this.handleSubmitDirect.bind(this);\n this.handleSubmit = debounce(200, this.handleSubmitDirect).bind(this);\n }\n\n onDrop = (acceptedFiles, rejectedFiles) => {\n // console.log(\"drop\", acceptedFiles, rejectedFiles);\n\n acceptedFiles.forEach(file => {\n const reader = new FileReader();\n reader.onload = () => {\n const fileAsBinaryString = reader.result;\n this.handleChange(fileAsBinaryString);\n };\n reader.onabort = () => console.log(\"file reading was aborted\");\n reader.onerror = () => console.log(\"file reading has failed\");\n\n reader.readAsBinaryString(file);\n });\n };\n\n handleChange(newValue) {\n const base64Result = Base64.encodeURI(newValue);\n this.props.history.push(`./?code=${base64Result}`);\n this.handleSubmit();\n }\n\n handleSubmitDirect() {\n setTimeout(() => {\n const { code } = getCodeFromLocation({ location: this.props.location });\n this.setState(\n {\n source: `graph TB; Loading;`,\n status: \"loading\"\n },\n () => {\n setTimeout(() => {\n try {\n const source = transformJsStringToMermaidString(code);\n this.setState({\n source: source,\n status: \"loaded\",\n error: null\n });\n } catch (error) {\n this.setState({\n status: \"error\",\n error: error\n });\n }\n }, 0);\n }\n );\n }, 0);\n }\n\n componentDidMount() {\n try {\n const { code } = getCodeFromLocation({ location: this.props.location });\n this.handleChange(code);\n } catch (error) {\n this.setState({\n status: \"error\",\n error: error\n });\n }\n }\n\n render() {\n const { code } = getCodeFromLocation({ location: this.props.location });\n return (\n \n {({ getRootProps, getInputProps, isDragActive }) => {\n const inputProps = getInputProps();\n // console.log(inputProps);\n return (\n \n {isDragActive ? (\n
\n Drop file anywhere\n
\n ) : null}\n
\n
\n
\n \n \n {\" \"}\n Drop code files anywhere or\n \n {\" \"}\n \n
\n
\n\n
\n {this.state.error === null || this.state.error === undefined ? (\n
\n
\n
\n
\n
\n ) : (\n
\n )}\n
\n );\n }}\n \n );\n }\n}\n\nexport default App;\n","import React, { Component } from \"react\";\n// import logo from \"./logo.svg\";\nimport \"./App.css\";\n\nimport Editor from \"./editor\";\nimport GithubCorner from \"react-github-corner\";\nimport { BrowserRouter as Router, Route } from \"react-router-dom\";\n\nclass App extends Component {\n render() {\n return (\n \n \n \n \n \n \n );\n }\n}\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read http://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.1/8 is considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit http://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl)\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(, document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: http://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""}
--------------------------------------------------------------------------------
/docs/static/js/runtime~main.367d1e08.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c {
38 | try {
39 | const image = await getImageData(inputOptions);
40 | const closestStructures = await findClosestStructures(
41 | image,
42 | inputOptions.userId,
43 | 30
44 | );
45 | if (closestStructures.status != 200) {
46 | return closestStructures;
47 | }
48 |
49 | image.closestStructure = closestStructures.body[0];
50 | console.log(`INFO: closest structure Id: ${image.closestStructure.id}`);
51 | console.log(`INFO: closest structure Name: ${image.closestStructure.name}`);
52 | console.log(
53 | `INFO: closest structure Altitude: ${
54 | image.closestStructure.location.altitude
55 | }`
56 | );
57 | console.log(
58 | `INFO: closest structure Latitude: ${
59 | image.closestStructure.location.latitude
60 | }`
61 | );
62 | console.log(
63 | `INFO: closest structure Longitude: ${
64 | image.closestStructure.location.longitude
65 | }`
66 | );
67 | console.log(
68 | `INFO: closest structure params: ${JSON.stringify(
69 | image.closestStructure.parameters,
70 | null,
71 | 2
72 | )} `
73 | );
74 |
75 | if (isNil(image.mission.altitudeOffset) || !inputOptions.scene3D) {
76 | console.log(`INFO: using scene 2D`);
77 | image.location.altitude = image.closestStructure.altitude;
78 | image.location.pitch = 0;
79 | map(structure => {
80 | structure.altitude = image.closestStructure.altitude;
81 | }, closestStructures.body);
82 | console.log(
83 | `INFO: updating image and all structures altitude to ${
84 | image.closestStructure.altitude
85 | }`
86 | );
87 | }
88 | const geoOrigin = [
89 | image.closestStructure.latitude,
90 | image.closestStructure.longitude,
91 | image.closestStructure.altitude
92 | ];
93 | const scene = await createScene({
94 | images: [image],
95 | structures: closestStructures.body,
96 | geoOrigin: geoOrigin,
97 | buffer: inputOptions.structureBuffer,
98 | cameraOptions: {
99 | near: 0.5,
100 | far: 30
101 | }
102 | });
103 | if (inputOptions.saveScene) {
104 | await writeJsonFile(`${image.id}_locatorScene.json`, scene.toJSON());
105 | }
106 | let intersection = find(structure => {
107 | return doesIntersect(scene, image.id, structure.id);
108 | }, closestStructures.body);
109 | if (isNil(intersection)) {
110 | console.log("INFO: FOUND NO INTERSECTION FOR THE IMAGE !!");
111 | console.log("INFO: USING CLOSEST STRUCTURE TO THE IMAGE !!");
112 | intersection = image.closestStructure;
113 | } else {
114 | console.log(
115 | `INFO: Found intersection with structure name ${
116 | intersection.name
117 | } and structure id ${intersection.id}`
118 | );
119 | }
120 | if (inputOptions.updateDataBase) {
121 | let resultPosition = {};
122 | if (isNil(image.mission)) {
123 | image.mission = intersection.mission;
124 | }
125 | const resultValidity = await updateImageValidityWrapper({
126 | imageId: image.id,
127 | missionExecutionId: {
128 | connect: { id: image.mission.id }
129 | },
130 | structureIds: {
131 | connect: [{ id: intersection.id }]
132 | }
133 | });
134 | console.log(
135 | `INFO: updated ${image.id} with missionExecution ${
136 | image.mission.id
137 | } and structure ${intersection.name} with id ${intersection.id}`
138 | );
139 | if (intersection.model.type == "HighVoltagePowerLinePylon") {
140 | const position = {
141 | height: image.location.altitude - intersection.altitude,
142 | angleToPylonOrientation: Math.PI - image.location.yaw
143 | };
144 | resultPosition = await updateImagePositionWrapper(
145 | image.location.id,
146 | position
147 | );
148 | {
149 | console.log(
150 | `INFO: Image Position % HTB ${JSON.stringify(position, null, 2)}`
151 | );
152 | }
153 | }
154 | return { ...resultValidity, ...resultPosition };
155 | } else {
156 | return { status: 200, body: `intersection is ${intersection.id}` };
157 | }
158 | } catch (e) {
159 | console.log(e);
160 | }
161 | };
162 |
163 | export const handler = async (event, context, callback) => {
164 | let imageId = event.imageId;
165 | let userId = event.userId;
166 | if (isNil(imageId)) {
167 | imageId = event.body.imageId;
168 | userId = event.body.userId;
169 | }
170 |
171 | console.log(`IMAGEID :: ${imageId}`);
172 | console.log(`USERID :: ${userId}`);
173 | const result = await main({
174 | imageId: imageId,
175 | userId: userId,
176 | scene3D: true,
177 | structureBuffer: 5,
178 | saveScene: false,
179 | updateDataBase: true
180 | });
181 | // const orgas = await getOrganizationByUserWrapper(userId);
182 | // const funcName = process.env.IMAGE_INSPECTOR;
183 | // console.log("image inspector name", funcName);
184 | // await Promise.map(
185 | // orgas,
186 | // async orga => {
187 | // try {
188 | // console.log("running inspector for", orga.name);
189 | // const res = await invokeLambda(funcName)({
190 | // imageId: imageId,
191 | // organizationId: orga.id
192 | // });
193 | // console.log(res);
194 | // } catch (e) {
195 | // console.log(e);
196 | // }
197 | // },
198 | // { concurrency: 20 }
199 | // );
200 |
201 | callback(null, result);
202 | };
203 |
204 | const run = async () => {
205 | const userId = "cjod5xngh0s060170t1bss74x";
206 | const filter = {
207 | structures_some: { id: "cjik2b2pnkb5z01092b20912p" },
208 | mission: { mission: { id: "cjpjtczrn43on0c3229h75thl" } },
209 | file: { id_not: null }
210 | };
211 |
212 | const imageList = await getListImagesByFilter({
213 | filter: filter
214 | });
215 | console.log(`got ${imageList.images.length} images`);
216 | const funcName = `image-pipeline-${process.env.STAGE}-locatorHandler`;
217 | console.log("Using ", funcName);
218 | await Promise.map(
219 | imageList.images,
220 | async image => {
221 | try {
222 | const res = await handler(
223 | {
224 | imageId: image.id,
225 | userId: userId
226 | },
227 | null,
228 | function(aes, res) {
229 | console.log(res);
230 | }
231 | );
232 | // const res = await invokeLambda(funcName)({
233 | // imageId: image.id,
234 | // userId: userId
235 | // });
236 | // if (res.StatusCode == 200) {
237 | // const result = JSON.parse(res.Payload);
238 | // console.log(
239 | // "SUCCESS :: image:",
240 | // image.id,
241 | // "vaildity",
242 | // result[0].valid
243 | // );
244 | // } else {
245 | // console.log("ERROR :: image:", image.id);
246 | // }
247 | } catch (e) {
248 | console.log(e);
249 | }
250 | },
251 | { concurrency: 3 }
252 | );
253 | };
254 |
255 | if (require.main === module) {
256 | // run();
257 | handler(
258 | {
259 | imageId: "cjpo38p72m25j0c32chygfxod",
260 | userId: "cjod5xngh0s060170t1bss74x"
261 | },
262 | null,
263 | function(aes, res) {
264 | console.log(res);
265 | }
266 | );
267 | }
268 |
--------------------------------------------------------------------------------
/example.js:
--------------------------------------------------------------------------------
1 | import { toto } from "tutu";
2 |
3 | function tryCatchExample(args) {
4 | function f() {
5 | const d = async () => {
6 | await 5;
7 | return toto;
8 | };
9 | const ff = 45;
10 | try {
11 | let a = 5;
12 | a = a + 1;
13 | if (a > 3) {
14 | throw new Error("not normal");
15 | }
16 | } catch (e) {
17 | console.log("in the catch");
18 | if (v) return 5;
19 | if (ftw) throw new Error("Real bad");
20 | } finally {
21 | console.log("in the finally");
22 | if (u) return 8;
23 | }
24 | return 1;
25 | }
26 | }
27 |
28 | export async function* generatorExample(args) {
29 | yield 1;
30 | yield 5;
31 | if (myCond) {
32 | const c = yield 21;
33 | const d = await coco(c);
34 | } else {
35 | yield 25;
36 | let u = yield f(45 + 6);
37 | }
38 | for await (let a of b) {
39 | const b = await f(a);
40 | yield b + b;
41 | frameElement(2);
42 | const u = yield 6;
43 | }
44 | return d;
45 | }
46 |
47 | async function controlFlowExample(x) {
48 | const a = 5;
49 | const b = await toto(44);
50 | while (b > 0) {
51 | if (b) {
52 | return 5;
53 | } else if (c) {
54 | throw new Error("Not");
55 | } else {
56 | b = 52;
57 | }
58 | }
59 | do {
60 | b = b + 1;
61 | c = f(k);
62 | if (k) {
63 | break;
64 | }
65 | } while (b < 4);
66 | if (k++ == 5) {
67 | coucou2 = 24;
68 | }
69 | for (let t = 0; t < 5; t++) {
70 | if (l) {
71 | f(h);
72 | } else {
73 | webkitCancelAnimationFrame(32);
74 | }
75 | }
76 | return a * 3;
77 | }
78 |
79 | class ExampleClass {
80 | myMethod(x) {
81 | console.log("lol");
82 | return;
83 | }
84 | exampleMethod(x) {
85 | if (myCondition) return 5;
86 | return 6;
87 | }
88 | property = 8;
89 | static staticProp = 19;
90 | render() {
91 | return (
92 |
93 | Toto
94 | Titi
95 |
96 | );
97 | }
98 | }
99 |
100 | export default toto;
101 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const babelParser = require("@babel/parser");
2 |
3 | const fp = require("lodash/fp");
4 |
5 | const { transformGeneralAstToGraph } = require("./transform.js");
6 |
7 | function transformJsStringToJsAst(string) {
8 | return babelParser.parse(string, {
9 | sourceType: "module",
10 | plugins: ["classProperties", "asyncGenerators", "jsx", "typescript"]
11 | });
12 | }
13 |
14 | function transformJsAstToGraph(ast) {
15 | // if (
16 | // ast.type === "FunctionExpression" ||
17 | // ast.type === "ArrowFunctionExpression"
18 | // ) {
19 | return transformGeneralAstToGraph(ast);
20 | // } else if (ast){}else{
21 | // console.log(JSON.stringify(ast, null, 2));
22 | // throw new Error(`Code must be a FunctionExpression, but is a ${ast.type}`);
23 | // }
24 | }
25 |
26 | function transformNodeToMermaidString({ id, name, shape = "square", style }) {
27 | const styleText = fp.isObject(style)
28 | ? `\n style ${id} ${fp.join(
29 | ",",
30 | fp.map(
31 | ([key, value]) => `${fp.kebabCase(key)}:${value}`,
32 | fp.toPairs(style)
33 | )
34 | )}`
35 | : "";
36 | const text = fp.isString(name) ? name : id;
37 | switch (shape) {
38 | case "round":
39 | return ` ${id}(${text})${styleText}`;
40 | case "circle":
41 | return ` ${id}((${text}))${styleText}`;
42 | case "asymetric":
43 | return ` ${id}>${text}]${styleText}`;
44 | case "rhombus":
45 | return ` ${id}{${text}}${styleText}`;
46 | case "square":
47 | default:
48 | return ` ${id}[${text}]${styleText}`;
49 | }
50 | }
51 |
52 | function transformEdgeToMermaidString({
53 | from,
54 | to,
55 | name,
56 | type = "solid",
57 | arrow = true
58 | }) {
59 | const text = fp.isString(name) ? name : "";
60 | const arrowText = arrow ? ">" : "";
61 | if (fp.isEmpty(text)) {
62 | switch (type) {
63 | case "dotted":
64 | return ` ${from} -.-${arrowText} ${to}`;
65 | case "thick":
66 | return ` ${from} ==${arrowText} ${to}`;
67 | case "solid":
68 | default:
69 | return ` ${from} --${arrowText} ${to}`;
70 | }
71 | } else {
72 | switch (type) {
73 | case "dotted":
74 | return ` ${from} -. ${text} .-${arrowText} ${to}`;
75 | case "thick":
76 | return ` ${from} == ${text} ==${arrowText} ${to}`;
77 | case "solid":
78 | default:
79 | return ` ${from} -- ${text} --${arrowText} ${to}`;
80 | }
81 | }
82 | }
83 |
84 | function transformGraphFragmentToMermaidString({ nodes, edges, subGraphs }) {
85 | const nodesMermaidString = fp.join(
86 | "\n",
87 | fp.map(transformNodeToMermaidString, nodes)
88 | );
89 | const subGraphMermaidString = fp.join(
90 | "\n",
91 | fp.map(({ name, graph }) => {
92 | // console.log("SUBGRAPH", name);
93 | return (
94 | `subgraph ${name.replace("_", " ")}\n` +
95 | transformGraphFragmentToMermaidString(graph) +
96 | `\nend`
97 | );
98 | }, subGraphs)
99 | );
100 | // console.log("subGraphMermaidString", subGraphMermaidString);
101 |
102 | const edgesMermaidString = fp.join(
103 | "\n",
104 | fp.map(transformEdgeToMermaidString, edges)
105 | );
106 |
107 | return (
108 | nodesMermaidString +
109 | (fp.isEmpty(subGraphs) ? "" : "\n" + subGraphMermaidString) +
110 | "\n" +
111 | edgesMermaidString
112 | );
113 | }
114 |
115 | function transformGraphToMermaidString(
116 | { nodes, edges, subGraphs },
117 | { direction = "TD" } = {}
118 | ) {
119 | const graphMermaidString = `graph ${direction}`;
120 | const graphFragmentMermaidString = transformGraphFragmentToMermaidString({
121 | nodes,
122 | edges,
123 | subGraphs
124 | });
125 | return graphMermaidString + "\n" + graphFragmentMermaidString;
126 | }
127 |
128 | function transformJsStringToMermaidString(string) {
129 | return transformGraphToMermaidString(
130 | transformJsAstToGraph(transformJsStringToJsAst(string))
131 | );
132 | }
133 |
134 | module.exports = { transformJsStringToMermaidString, transformJsStringToJsAst };
135 |
--------------------------------------------------------------------------------
/index.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | transformJsStringToMermaidString,
3 | transformJsStringToJsAst
4 | } = require(".");
5 |
6 | test("1", () => {
7 | const res = transformJsStringToMermaidString("function f(x){return 5;}");
8 | // console.log(res);
9 | expect(res).toEqual(
10 | `graph TD
11 |
12 | subgraph f
13 | statementfroml1c14tol1c23>"return 5;"]
14 | style statementfroml1c14tol1c23 fill:#99FF99
15 |
16 | end
17 | `
18 | );
19 | });
20 |
21 | test("2", () => {
22 | const res = transformJsStringToMermaidString("(x)=>{return 5;}");
23 | // console.log(res);
24 | expect(res).toEqual(`graph TD
25 |
26 | subgraph statementfroml1c0tol1c16
27 | statementfroml1c6tol1c15>"return 5;"]
28 | style statementfroml1c6tol1c15 fill:#99FF99
29 |
30 | end
31 | `);
32 | });
33 |
34 | test("3", () => {
35 | const res = transformJsStringToMermaidString(
36 | "(x)=>{if(x===0){return 5;}else{return 4}}"
37 | );
38 | // console.log(res);
39 | expect(res).toEqual(`graph TD
40 |
41 | subgraph statementfroml1c0tol1c41
42 | statementfroml1c6tol1c40{"if x === 0"}
43 | statementfroml1c16tol1c25>"return 5;"]
44 | style statementfroml1c16tol1c25 fill:#99FF99
45 | statementfroml1c31tol1c39>"return 4;"]
46 | style statementfroml1c31tol1c39 fill:#99FF99
47 | statementfroml1c6tol1c40 -- true --> statementfroml1c16tol1c25
48 | statementfroml1c6tol1c40 -- false --> statementfroml1c31tol1c39
49 | end
50 | `);
51 | });
52 |
53 | test("4", () => {
54 | const res = transformJsStringToMermaidString(
55 | "(x)=>{const a=f(x);if(x===0){return 5;}else{const c=8;return 4}}"
56 | );
57 | // console.log(res);
58 |
59 | expect(res).toEqual(`graph TD
60 |
61 | subgraph statementfroml1c0tol1c64
62 | statementfroml1c6tol1c19("const a = f(x);")
63 | statementfroml1c19tol1c63{"if x === 0"}
64 | statementfroml1c29tol1c38>"return 5;"]
65 | style statementfroml1c29tol1c38 fill:#99FF99
66 | statementfroml1c44tol1c54("const c = 8;")
67 | statementfroml1c54tol1c62>"return 4;"]
68 | style statementfroml1c54tol1c62 fill:#99FF99
69 | statementfroml1c19tol1c63 -- true --> statementfroml1c29tol1c38
70 | statementfroml1c19tol1c63 -- false --> statementfroml1c44tol1c54
71 | statementfroml1c44tol1c54 --> statementfroml1c54tol1c62
72 | statementfroml1c6tol1c19 --> statementfroml1c19tol1c63
73 | end
74 | `);
75 | });
76 |
77 | test("5", () => {
78 | const res = transformJsStringToMermaidString(
79 | "const g = (x)=>{const a=f(x);if(x===0){return 5;}else{const c=8;return 4}}"
80 | );
81 | // console.log(res);
82 |
83 | expect(res).toEqual(`graph TD
84 |
85 | subgraph g
86 | statementfroml1c16tol1c29("const a = f(x);")
87 | statementfroml1c29tol1c73{"if x === 0"}
88 | statementfroml1c39tol1c48>"return 5;"]
89 | style statementfroml1c39tol1c48 fill:#99FF99
90 | statementfroml1c54tol1c64("const c = 8;")
91 | statementfroml1c64tol1c72>"return 4;"]
92 | style statementfroml1c64tol1c72 fill:#99FF99
93 | statementfroml1c29tol1c73 -- true --> statementfroml1c39tol1c48
94 | statementfroml1c29tol1c73 -- false --> statementfroml1c54tol1c64
95 | statementfroml1c54tol1c64 --> statementfroml1c64tol1c72
96 | statementfroml1c16tol1c29 --> statementfroml1c29tol1c73
97 | end
98 | `);
99 | });
100 |
101 | test("6", () => {
102 | const res = transformJsStringToMermaidString(
103 | "const g = function k(x){const a=w(x);if(x===0){return 6;}else{const c=8;return 4}}"
104 | );
105 | // console.log(res);
106 |
107 | expect(res).toEqual(`graph TD
108 |
109 | subgraph g
110 | statementfroml1c24tol1c37("const a = w(x);")
111 | statementfroml1c37tol1c81{"if x === 0"}
112 | statementfroml1c47tol1c56>"return 6;"]
113 | style statementfroml1c47tol1c56 fill:#99FF99
114 | statementfroml1c62tol1c72("const c = 8;")
115 | statementfroml1c72tol1c80>"return 4;"]
116 | style statementfroml1c72tol1c80 fill:#99FF99
117 | statementfroml1c37tol1c81 -- true --> statementfroml1c47tol1c56
118 | statementfroml1c37tol1c81 -- false --> statementfroml1c62tol1c72
119 | statementfroml1c62tol1c72 --> statementfroml1c72tol1c80
120 | statementfroml1c24tol1c37 --> statementfroml1c37tol1c81
121 | end
122 | `);
123 | });
124 |
125 | test("7", () => {
126 | const res = transformJsStringToMermaidString(
127 | `(x)=>{const a=f(x);if(x===0){let a=null; throw new Error("Nooes")}else{const c=8;return 4}}`
128 | );
129 | // console.log(res);
130 |
131 | expect(res).toEqual(`graph TD
132 |
133 | subgraph statementfroml1c0tol1c91
134 | statementfroml1c6tol1c19("const a = f(x);")
135 | statementfroml1c19tol1c90{"if x === 0"}
136 | statementfroml1c29tol1c40("let a = null;")
137 | statementfroml1c41tol1c65>"throw new Error('Nooes');"]
138 | style statementfroml1c41tol1c65 fill:#FF9999
139 | statementfroml1c71tol1c81("const c = 8;")
140 | statementfroml1c81tol1c89>"return 4;"]
141 | style statementfroml1c81tol1c89 fill:#99FF99
142 | statementfroml1c19tol1c90 -- true --> statementfroml1c29tol1c40
143 | statementfroml1c19tol1c90 -- false --> statementfroml1c71tol1c81
144 | statementfroml1c29tol1c40 --> statementfroml1c41tol1c65
145 | statementfroml1c71tol1c81 --> statementfroml1c81tol1c89
146 | statementfroml1c6tol1c19 --> statementfroml1c19tol1c90
147 | end
148 | `);
149 | });
150 |
151 | test("8", () => {
152 | const res = transformJsStringToMermaidString(
153 | `const q = (x)=>{switch(x){case 0: return 1;case 2:{const a=1;return 3;};default: return 4}}`
154 | );
155 | // console.log(res);
156 |
157 | expect(res).toEqual(`graph TD
158 |
159 | subgraph q
160 | statementfroml1c16tol1c90{"switch x "}
161 | statementfroml1c34tol1c43>"return 1;"]
162 | style statementfroml1c34tol1c43 fill:#99FF99
163 | statementfroml1c51tol1c61("const a = 1;")
164 | statementfroml1c61tol1c70>"return 3;"]
165 | style statementfroml1c61tol1c70 fill:#99FF99
166 | statementfroml1c71tol1c72["Empty statement at line 1 column 71"]
167 | statementfroml1c81tol1c89>"return 4;"]
168 | style statementfroml1c81tol1c89 fill:#99FF99
169 | statementfroml1c16tol1c90 -- 0 --> statementfroml1c34tol1c43
170 | statementfroml1c51tol1c61 --> statementfroml1c61tol1c70
171 | statementfroml1c16tol1c90 -- 2 --> statementfroml1c51tol1c61
172 | statementfroml1c16tol1c90 -- default --> statementfroml1c81tol1c89
173 | statementfroml1c71tol1c72 --> statementfroml1c81tol1c89
174 | end
175 | `);
176 | });
177 |
178 | test("9", () => {
179 | const res = transformJsStringToMermaidString(
180 | `const gogo = function(x){
181 | let y = 9;
182 | switch(x){
183 | case 0: throw new Error("coco");
184 | case 1: return 1;
185 | case 2:{const a=98;return 3;};
186 | case 3: y =5;
187 | case 4: y=9; break;
188 | default: return 4
189 | }
190 | console.log(y);
191 | return y + 1;
192 | }`
193 | );
194 | // console.log(res);
195 |
196 | expect(res).toEqual(`graph TD
197 |
198 | subgraph gogo
199 | statementfroml2c10tol2c20("let y = 9;")
200 | statementfroml3c10tol10c11{"switch x "}
201 | statementfroml4c22tol4c46>"throw new Error('coco');"]
202 | style statementfroml4c22tol4c46 fill:#FF9999
203 | statementfroml5c22tol5c31>"return 1;"]
204 | style statementfroml5c22tol5c31 fill:#99FF99
205 | statementfroml6c22tol6c33("const a = 98;")
206 | statementfroml6c33tol6c42>"return 3;"]
207 | style statementfroml6c33tol6c42 fill:#99FF99
208 | statementfroml6c43tol6c44["Empty statement at line 6 column 43"]
209 | statementfroml7c22tol7c27("y = 5;")
210 | statementfroml8c22tol8c26("y = 9;")
211 | statementfroml8c27tol8c33["break;"]
212 | statementfroml9c23tol9c31>"return 4;"]
213 | style statementfroml9c23tol9c31 fill:#99FF99
214 | statementfroml11c10tol11c25("console.log(y);")
215 | statementfroml12c10tol12c23>"return y + 1;"]
216 | style statementfroml12c10tol12c23 fill:#99FF99
217 | statementfroml3c10tol10c11 -- 0 --> statementfroml4c22tol4c46
218 | statementfroml3c10tol10c11 -- 1 --> statementfroml5c22tol5c31
219 | statementfroml6c22tol6c33 --> statementfroml6c33tol6c42
220 | statementfroml3c10tol10c11 -- 2 --> statementfroml6c22tol6c33
221 | statementfroml3c10tol10c11 -- 3 --> statementfroml7c22tol7c27
222 | statementfroml6c43tol6c44 --> statementfroml7c22tol7c27
223 | statementfroml8c22tol8c26 --> statementfroml8c27tol8c33
224 | statementfroml3c10tol10c11 -- 4 --> statementfroml8c22tol8c26
225 | statementfroml7c22tol7c27 --> statementfroml8c22tol8c26
226 | statementfroml3c10tol10c11 -- default --> statementfroml9c23tol9c31
227 | statementfroml2c10tol2c20 --> statementfroml3c10tol10c11
228 | statementfroml8c27tol8c33 --> statementfroml11c10tol11c25
229 | statementfroml11c10tol11c25 --> statementfroml12c10tol12c23
230 | end
231 | `);
232 | });
233 |
--------------------------------------------------------------------------------
/mermaid-diagram-example.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code-to-graph",
3 | "version": "1.0.15",
4 | "main": "index.js",
5 | "repository": "https://github.com/crubier/code-to-graph.git",
6 | "author": "Vincent Lecrubier ",
7 | "license": "MIT",
8 | "scripts": {
9 | "test": "jest",
10 | "postpublish": "cd docs-source && yarn install && yarn docs"
11 | },
12 | "bin": {
13 | "code-to-graph": "./cli.js"
14 | },
15 | "dependencies": {
16 | "@babel/generator": "^7.2.2",
17 | "@babel/parser": "^7.2.3",
18 | "lodash": "^4.17.11",
19 | "mermaid": "^8.0.0",
20 | "opn": "^5.4.0"
21 | },
22 | "devDependencies": {
23 | "jest": "^23.6.0"
24 | }
25 | }
--------------------------------------------------------------------------------
/parser.test.js:
--------------------------------------------------------------------------------
1 | const {
2 | transformJsStringToMermaidString,
3 | transformJsStringToJsAst
4 | } = require(".");
5 |
6 | describe("parser", () => {
7 | test("return", () => {
8 | expect(
9 | transformJsStringToJsAst("function f(x){return 5;}")
10 | ).toMatchSnapshot();
11 | });
12 |
13 | test("return", () => {
14 | expect(
15 | transformJsStringToJsAst("function f(x){const a = 5;}")
16 | ).toMatchSnapshot();
17 | });
18 |
19 | test("if else", () => {
20 | expect(
21 | transformJsStringToJsAst(
22 | "function f(x){if(x===5){return 0}else{return 1}}"
23 | )
24 | ).toMatchSnapshot();
25 | });
26 |
27 | test("if else", () => {
28 | expect(
29 | transformJsStringToJsAst(
30 | "function f(x){if(x===5){return 0}else if(x===6){return 1}else {return 2}}"
31 | )
32 | ).toMatchSnapshot();
33 | });
34 |
35 | test("switch", () => {
36 | expect(
37 | transformJsStringToJsAst(
38 | "function f(x){switch(x){case 1:return 2; default: break;}return 3;}"
39 | )
40 | ).toMatchSnapshot();
41 | });
42 |
43 | test("switch", () => {
44 | expect(
45 | transformJsStringToJsAst(
46 | `(x)=>{switch(x){case 0: return 1;case 2:{const a=1;return 3;};default: return 4}}`
47 | )
48 | ).toMatchSnapshot();
49 | });
50 |
51 | test("yield", () => {
52 | expect(
53 | transformJsStringToJsAst("function* f(x){yield 5;}")
54 | ).toMatchSnapshot();
55 | });
56 |
57 | test("const yield", () => {
58 | expect(
59 | transformJsStringToJsAst("function* f(x){const a = yield 5;}")
60 | ).toMatchSnapshot();
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/transform.js:
--------------------------------------------------------------------------------
1 | const { default: generate } = require("@babel/generator");
2 | const fp = require("lodash/fp");
3 |
4 | function makeIdFromAstNode(astNode) {
5 | return `froml${astNode.loc.start.line}c${astNode.loc.start.column}tol${
6 | astNode.loc.end.line
7 | }c${astNode.loc.end.column}`;
8 | }
9 |
10 | function transformGeneralAstToGraph(ast) {
11 | if (fp.isArray(ast)) {
12 | return transformStatementSequenceToGraph(ast);
13 | } else if (fp.isObject(ast)) {
14 | return transformStatementToGraph(ast);
15 | } else {
16 | return transformStatementToGraph(ast);
17 | }
18 | }
19 |
20 | const emptyGraph = {
21 | nodes: [],
22 | edges: [],
23 | entryNodes: [],
24 | exitNodes: [],
25 | breakNodes: [],
26 | subGraphs: []
27 | };
28 |
29 | function transformStatementSequenceToGraph(statements) {
30 | return fp.reduce(
31 | (
32 | { nodes, edges, entryNodes, exitNodes, breakNodes, subGraphs },
33 | {
34 | nodes: currentNodes,
35 | edges: currentEdges,
36 | entryNodes: currentEntryNodes,
37 | exitNodes: currentExitNodes,
38 | breakNodes: currentBreakNodes,
39 | subGraphs: currentSubGraphs
40 | }
41 | ) => ({
42 | nodes: [...nodes, ...currentNodes],
43 | edges: fp.compact([
44 | ...edges,
45 | ...currentEdges,
46 | ...fp.flatten(
47 | fp.map(
48 | exitNode =>
49 | fp.map(
50 | entryNode => ({
51 | from: exitNode.id,
52 | to: entryNode.id,
53 | name: "",
54 | type: "solid",
55 | arrow: true
56 | }),
57 | currentEntryNodes
58 | ),
59 | exitNodes
60 | )
61 | )
62 | ]),
63 | entryNodes: fp.isEmpty(entryNodes) ? currentEntryNodes : entryNodes,
64 | exitNodes: currentExitNodes,
65 | breakNodes: [...breakNodes, ...currentBreakNodes],
66 | subGraphs: [...subGraphs, ...currentSubGraphs]
67 | }),
68 | {
69 | nodes: [],
70 | edges: [],
71 | entryNodes: [],
72 | exitNodes: [],
73 | breakNodes: [],
74 | subGraphs: []
75 | },
76 | fp.map(transformGeneralAstToGraph, statements)
77 | );
78 | }
79 |
80 | function transformStatementToGraph(statement) {
81 | try {
82 | switch (statement.type) {
83 | case "TSTypeAliasDeclaration": {
84 | return emptyGraph;
85 | }
86 | case "CommentLine": {
87 | return emptyGraph;
88 | }
89 | case "File": {
90 | return transformGeneralAstToGraph(statement.program);
91 | }
92 | case "Program": {
93 | return transformGeneralAstToGraph(statement.body);
94 | }
95 | case "ClassProperty": {
96 | const node = cleanGraphNode({
97 | id: makeIdFromAstNode(statement),
98 | name: generate(statement).code,
99 | shape: "round"
100 | });
101 | return {
102 | nodes: [node],
103 | edges: [],
104 | entryNodes: [],
105 | exitNodes: [],
106 | breakNodes: [],
107 | subGraphs: []
108 | };
109 | }
110 | case "ClassDeclaration": {
111 | return {
112 | nodes: [],
113 | edges: [],
114 | entryNodes: [],
115 | exitNodes: [],
116 | breakNodes: [],
117 | subGraphs: [
118 | {
119 | name: `class_${
120 | fp.isNil(statement.id)
121 | ? makeIdFromAstNode(statement)
122 | : generate(statement.id).code
123 | }`,
124 | graph: transformGeneralAstToGraph(statement.body)
125 | }
126 | ]
127 | };
128 | }
129 | case "ClassBody": {
130 | return transformGeneralAstToGraph(statement.body);
131 | }
132 | case "FunctionExpression": {
133 | return transformGeneralAstToGraph(statement.body);
134 | }
135 | case "ArrowFunctionExpression": {
136 | return transformGeneralAstToGraph(statement.body);
137 | }
138 | case "ClassMethod": {
139 | return {
140 | nodes: [],
141 | edges: [],
142 | entryNodes: [],
143 | exitNodes: [],
144 | breakNodes: [],
145 | subGraphs: [
146 | {
147 | name: `method_${
148 | fp.isNil(statement.key)
149 | ? makeIdFromAstNode(statement)
150 | : generate(statement.key).code
151 | }`,
152 | graph: transformGeneralAstToGraph(statement.body)
153 | }
154 | ]
155 | };
156 | }
157 | case "FunctionDeclaration": {
158 | // console.log("FunctionDeclaration");
159 | // console.log(statement);
160 | return {
161 | nodes: [],
162 | edges: [],
163 | entryNodes: [],
164 | exitNodes: [],
165 | breakNodes: [],
166 | subGraphs: [
167 | {
168 | name: `function_${
169 | fp.isNil(statement.id)
170 | ? makeIdFromAstNode(statement)
171 | : generate(statement.id).code
172 | }`,
173 | graph: transformGeneralAstToGraph(statement.body)
174 | }
175 | ]
176 | };
177 | }
178 | case "BlockStatement": {
179 | return transformGeneralAstToGraph(statement.body);
180 | }
181 |
182 | case "VariableDeclarator": {
183 | // console.log("VariableDeclarator");
184 | const node = cleanGraphNode({
185 | id: makeIdFromAstNode(statement),
186 | name: generate(statement).code,
187 | shape: "round"
188 | });
189 | return {
190 | nodes: [node],
191 | edges: [],
192 | entryNodes: [node],
193 | exitNodes: [node],
194 | breakNodes: [],
195 | subGraphs: []
196 | };
197 | }
198 | case "VariableDeclaration": {
199 | // console.log("VariableDeclaration");
200 | // console.log(statement);
201 |
202 | const literalsDeclarators = fp.filter(declarator => {
203 | if (declarator.init.type === "FunctionExpression") {
204 | return false;
205 | } else if (declarator.init.type === "ArrowFunctionExpression") {
206 | return false;
207 | } else if (declarator.init.type === "YieldExpression") {
208 | return false;
209 | } else {
210 | return true;
211 | }
212 | }, statement.declarations);
213 |
214 | const literalsDeclarations = {
215 | ...statement,
216 | declarations: [...literalsDeclarators]
217 | };
218 |
219 | const literalsNodes = fp.isEmpty(literalsDeclarations.declarations)
220 | ? []
221 | : [
222 | cleanGraphNode({
223 | id: makeIdFromAstNode(literalsDeclarations),
224 | name: generate(literalsDeclarations).code,
225 | shape: "round"
226 | })
227 | ];
228 |
229 | const functionsDeclarators = fp.filter(declarator => {
230 | if (declarator.init.type === "FunctionExpression") {
231 | return true;
232 | } else if (declarator.init.type === "ArrowFunctionExpression") {
233 | return true;
234 | } else if (declarator.init.type === "YieldExpression") {
235 | return false;
236 | } else {
237 | return false;
238 | }
239 | }, statement.declarations);
240 |
241 | // console.log("functionsDeclarators", functionsDeclarators);
242 |
243 | const functionsSubGraphs = fp.reduce(
244 | ({ subGraphs }, declarator) => {
245 | return {
246 | subGraphs: [
247 | ...subGraphs,
248 | {
249 | name: `function_${declarator.id.name}`,
250 | graph: transformGeneralAstToGraph(declarator.init)
251 | }
252 | ]
253 | };
254 | },
255 | {
256 | subGraphs: []
257 | },
258 | functionsDeclarators
259 | );
260 |
261 | const yieldDeclarators = fp.filter(declarator => {
262 | if (declarator.init.type === "FunctionExpression") {
263 | return false;
264 | } else if (declarator.init.type === "ArrowFunctionExpression") {
265 | return false;
266 | } else if (declarator.init.type === "YieldExpression") {
267 | // console.log("YIELLLDD");
268 | // console.log(declarator);
269 | // console.log("");
270 | return true;
271 | } else {
272 | return false;
273 | }
274 | }, statement.declarations);
275 |
276 | const yieldDeclarations = {
277 | ...statement,
278 | declarations: [...yieldDeclarators]
279 | };
280 |
281 | const yieldNodes = fp.isEmpty(yieldDeclarations.declarations)
282 | ? []
283 | : [
284 | cleanGraphNode({
285 | id: makeIdFromAstNode(statement),
286 | name: generate(statement).code,
287 | shape: "asymetric",
288 | style: { fill: "#99CCFF" }
289 | })
290 | ];
291 |
292 | return {
293 | nodes: [...literalsNodes, ...yieldNodes],
294 | edges: [],
295 | entryNodes: [...literalsNodes, ...yieldNodes],
296 | exitNodes: [...literalsNodes, ...yieldNodes],
297 | breakNodes: [],
298 | subGraphs: [...functionsSubGraphs.subGraphs]
299 | };
300 | }
301 | case "ExpressionStatement": {
302 | if (statement.expression.type === "ArrowFunctionExpression") {
303 | return {
304 | nodes: [],
305 | edges: [],
306 | entryNodes: [],
307 | exitNodes: [],
308 | breakNodes: [],
309 | subGraphs: [
310 | {
311 | name: `function_${makeIdFromAstNode(statement)}`,
312 | graph: transformGeneralAstToGraph(statement.expression.body)
313 | }
314 | ]
315 | };
316 | } else if (statement.expression.type === "FunctionExpression") {
317 | // console.log("FunctionExpression");
318 | return {
319 | nodes: [],
320 | edges: [],
321 | entryNodes: [],
322 | exitNodes: [],
323 | breakNodes: [],
324 | subGraphs: [
325 | {
326 | name: `function_${statement.name}`,
327 | graph: transformGeneralAstToGraph(statement.body)
328 | }
329 | ]
330 | };
331 | } else if (statement.expression.type === "YieldExpression") {
332 | const node = cleanGraphNode({
333 | id: makeIdFromAstNode(statement),
334 | name: generate(statement).code,
335 | shape: "asymetric",
336 | style: { fill: "#99CCFF" }
337 | });
338 | return {
339 | nodes: [node],
340 | edges: [],
341 | entryNodes: [node],
342 | exitNodes: [node],
343 | breakNodes: [],
344 | subGraphs: []
345 | };
346 | } else {
347 | const node = cleanGraphNode({
348 | id: makeIdFromAstNode(statement),
349 | name: generate(statement).code,
350 | shape: "round"
351 | });
352 | return {
353 | nodes: [node],
354 | edges: [],
355 | entryNodes: [node],
356 | exitNodes: [node],
357 | breakNodes: [],
358 | subGraphs: []
359 | };
360 | }
361 | }
362 | case "CatchClause": {
363 | return transformGeneralAstToGraph(statement.body);
364 | }
365 | case "TryStatement": {
366 | // statement.finalizer;
367 | // statement.handler;
368 | // statement.block;
369 | const {
370 | nodes: blockNodes,
371 | edges: blockEdges,
372 | entryNodes: blockEntryNodes,
373 | exitNodes: blockExitNodes,
374 | breakNodes: blockBreakNodes,
375 | subGraphs: blockSubGraphs
376 | } = fp.isObject(statement.block)
377 | ? transformGeneralAstToGraph(statement.block)
378 | : emptyGraph;
379 | const {
380 | nodes: finalizerNodes,
381 | edges: finalizerEdges,
382 | entryNodes: finalizerEntryNodes,
383 | exitNodes: finalizerExitNodes,
384 | breakNodes: finalizerBreakNodes,
385 | subGraphs: finalizerSubGraphs
386 | } = fp.isObject(statement.finalizer)
387 | ? transformGeneralAstToGraph(statement.finalizer)
388 | : emptyGraph;
389 | const {
390 | nodes: handlerNodes,
391 | edges: handlerEdges,
392 | entryNodes: handlerEntryNodes,
393 | exitNodes: handlerExitNodes,
394 | breakNodes: handlerBreakNodes,
395 | subGraphs: handlerSubGraphs
396 | } = fp.isObject(statement.handler)
397 | ? transformGeneralAstToGraph(statement.handler)
398 | : emptyGraph;
399 | const blockToFinallyEdges = fp.isObject(statement.finalizer)
400 | ? fp.flatten(
401 | fp.map(blockExitNode => {
402 | return fp.map(finalizerEntryNode => {
403 | // console.log("blockExitNode", blockExitNode);
404 | // console.log("finalizerEntryNode", finalizerEntryNode);
405 | return {
406 | from: blockExitNode.id,
407 | to: finalizerEntryNode.id,
408 | name: "",
409 | type: "solid",
410 | arrow: true
411 | };
412 | }, finalizerEntryNodes);
413 | }, blockExitNodes)
414 | )
415 | : [];
416 | const handlerToFinallyEdges = fp.isObject(statement.finalizer)
417 | ? fp.flatten(
418 | fp.map(handlerExitNode => {
419 | return fp.map(finalizerEntryNode => {
420 | // console.log("handlerExitNode", handlerExitNode);
421 | // console.log("finalizerEntryNode", finalizerEntryNode);
422 | return {
423 | from: handlerExitNode.id,
424 | to: finalizerEntryNode.id,
425 | name: "",
426 | type: "solid",
427 | arrow: true
428 | };
429 | }, finalizerEntryNodes);
430 | }, handlerExitNodes)
431 | )
432 | : [];
433 | const blockToHandlerEdges = fp.isObject(statement.handler)
434 | ? fp.flatten(
435 | fp.map(blockNode => {
436 | return fp.map(handlerEntryNode => {
437 | // console.log("blockNode", blockNode);
438 | // console.log("handlerEntryNode", handlerEntryNode);
439 | return {
440 | from: blockNode.id,
441 | to: handlerEntryNode.id,
442 | name: "error",
443 | type: "dotted",
444 | arrow: true
445 | };
446 | }, handlerEntryNodes);
447 | }, blockNodes)
448 | )
449 | : [];
450 | // console.log("blockToFinallyEdges", blockToFinallyEdges);
451 | return {
452 | nodes: [],
453 | edges: [
454 | ...blockToFinallyEdges,
455 | ...handlerToFinallyEdges,
456 | ...blockToHandlerEdges
457 | ],
458 | entryNodes: [...blockEntryNodes],
459 | exitNodes: fp.isObject(statement.finalizer)
460 | ? [...finalizerExitNodes]
461 | : [...blockExitNodes],
462 | breakNodes: [...finalizerBreakNodes, ...blockBreakNodes],
463 | subGraphs: [
464 | ...(fp.isObject(statement.block)
465 | ? [
466 | {
467 | name: `try_${makeIdFromAstNode(statement.block)}`,
468 | graph: {
469 | nodes: blockNodes,
470 | edges: blockEdges,
471 | entryNodes: blockEntryNodes,
472 | exitNodes: blockExitNodes,
473 | breakNodes: blockBreakNodes,
474 | subGraphs: blockSubGraphs
475 | }
476 | }
477 | ]
478 | : []),
479 | ...(fp.isObject(statement.finalizer)
480 | ? [
481 | {
482 | name: `finally_${makeIdFromAstNode(statement.finalizer)}`,
483 | graph: {
484 | nodes: finalizerNodes,
485 | edges: finalizerEdges,
486 | entryNodes: finalizerEntryNodes,
487 | exitNodes: finalizerExitNodes,
488 | breakNodes: finalizerBreakNodes,
489 | subGraphs: finalizerSubGraphs
490 | }
491 | }
492 | ]
493 | : []),
494 | ...(fp.isObject(statement.handler)
495 | ? [
496 | {
497 | name: `catch_${makeIdFromAstNode(statement.handler)}`,
498 | graph: {
499 | nodes: handlerNodes,
500 | edges: handlerEdges,
501 | entryNodes: handlerEntryNodes,
502 | exitNodes: handlerExitNodes,
503 | breakNodes: handlerBreakNodes,
504 | subGraphs: handlerSubGraphs
505 | }
506 | }
507 | ]
508 | : [])
509 | ]
510 | };
511 | }
512 | case "ExportNamedDeclaration":
513 | return transformGeneralAstToGraph(statement.declaration);
514 | case "ExportDefaultDeclaration":
515 | return transformGeneralAstToGraph(statement.declaration);
516 | case "Identifier":
517 | return {
518 | nodes: [],
519 | edges: [],
520 | entryNodes: [],
521 | exitNodes: [],
522 | breakNodes: [],
523 | subGraphs: []
524 | };
525 | case "ImportDeclaration":
526 | return {
527 | nodes: [],
528 | edges: [],
529 | entryNodes: [],
530 | exitNodes: [],
531 | breakNodes: [],
532 | subGraphs: []
533 | };
534 | case "EmptyStatement": {
535 | const node = cleanGraphNode({
536 | id: makeIdFromAstNode(statement),
537 | name: `Empty statement at line ${statement.loc.start.line} column ${
538 | statement.loc.start.column
539 | }`,
540 | shape: "square"
541 | });
542 | return {
543 | nodes: [node],
544 | edges: [],
545 | entryNodes: [node],
546 | exitNodes: [node],
547 | breakNodes: [],
548 | subGraphs: []
549 | };
550 | }
551 | case "ReturnStatement": {
552 | const node = cleanGraphNode({
553 | id: makeIdFromAstNode(statement),
554 | name: generate(statement).code,
555 | shape: "asymetric",
556 | style: { fill: "#99FF99" }
557 | });
558 | return {
559 | nodes: [node],
560 | edges: [],
561 | entryNodes: [node],
562 | exitNodes: [],
563 | breakNodes: [],
564 | subGraphs: []
565 | };
566 | }
567 | case "ThrowStatement": {
568 | const node = cleanGraphNode({
569 | id: makeIdFromAstNode(statement),
570 | name: generate(statement).code,
571 | shape: "asymetric",
572 | style: { fill: "#FF9999" }
573 | });
574 |
575 | return {
576 | nodes: [node],
577 | edges: [],
578 | entryNodes: [node],
579 | exitNodes: [],
580 | breakNodes: [],
581 | subGraphs: []
582 | };
583 | }
584 | case "BreakStatement": {
585 | const node = cleanGraphNode({
586 | id: makeIdFromAstNode(statement),
587 | name: generate(statement).code,
588 | shape: "square"
589 | });
590 | return {
591 | nodes: [node],
592 | edges: [],
593 | entryNodes: [node],
594 | exitNodes: [],
595 | breakNodes: [node],
596 | subGraphs: []
597 | };
598 | }
599 | case "DoWhileStatement":
600 | const thisNode = cleanGraphNode({
601 | id: makeIdFromAstNode(statement),
602 | name: `do while ${generate(statement.test).code}`,
603 | shape: "rhombus"
604 | });
605 | const {
606 | nodes: bodyNodes,
607 | edges: bodyEdges,
608 | entryNodes: bodyEntryNodes,
609 | exitNodes: bodyExitNodes,
610 | breakNodes: bodyBreakNodes
611 | } = transformGeneralAstToGraph(statement.body);
612 |
613 | const thisEdges = [
614 | ...fp.map(
615 | node => ({
616 | to: thisNode.id,
617 | from: node.id,
618 | name: "do",
619 | type: "solid",
620 | arrow: true
621 | }),
622 | bodyExitNodes
623 | ),
624 | ...fp.map(
625 | node => ({
626 | to: node.id,
627 | from: thisNode.id,
628 | name: "loop",
629 | type: "solid",
630 | arrow: true
631 | }),
632 | bodyEntryNodes
633 | )
634 | ];
635 | return {
636 | nodes: [thisNode, ...bodyNodes],
637 | edges: [...thisEdges, ...bodyEdges],
638 | entryNodes: [...bodyEntryNodes],
639 | exitNodes: [...bodyBreakNodes, thisNode],
640 | breakNodes: [],
641 | subGraphs: []
642 | };
643 | case "WhileStatement": {
644 | const thisNode = cleanGraphNode({
645 | id: makeIdFromAstNode(statement),
646 | name: `while ${generate(statement.test).code}`,
647 | shape: "rhombus"
648 | });
649 | const {
650 | nodes: bodyNodes,
651 | edges: bodyEdges,
652 | entryNodes: bodyEntryNodes,
653 | exitNodes: bodyExitNodes,
654 | breakNodes: bodyBreakNodes
655 | } = transformGeneralAstToGraph(statement.body);
656 |
657 | const thisEdges = [
658 | ...fp.map(
659 | node => ({
660 | from: thisNode.id,
661 | to: node.id,
662 | name: "do",
663 | type: "solid",
664 | arrow: true
665 | }),
666 | bodyEntryNodes
667 | ),
668 | ...fp.map(
669 | node => ({
670 | from: node.id,
671 | to: thisNode.id,
672 | name: "loop",
673 | type: "solid",
674 | arrow: true
675 | }),
676 | bodyExitNodes
677 | )
678 | ];
679 | return {
680 | nodes: [thisNode, ...bodyNodes],
681 | edges: [...thisEdges, ...bodyEdges],
682 | entryNodes: [thisNode],
683 | exitNodes: [...bodyBreakNodes, thisNode],
684 | breakNodes: [],
685 | subGraphs: []
686 | };
687 | }
688 | case "ForOfStatement":
689 | case "ForStatement": {
690 | const thisNode = cleanGraphNode({
691 | id: makeIdFromAstNode(statement),
692 | name: `for ${generate(statement.init).code} ; ${
693 | generate(statement.test).code
694 | } ; ${generate(statement.update).code}`,
695 | shape: "rhombus"
696 | });
697 | const {
698 | nodes: bodyNodes,
699 | edges: bodyEdges,
700 | entryNodes: bodyEntryNodes,
701 | exitNodes: bodyExitNodes,
702 | breakNodes: bodyBreakNodes
703 | } = transformGeneralAstToGraph(statement.body);
704 |
705 | const thisEdges = [
706 | ...fp.map(
707 | node => ({
708 | from: thisNode.id,
709 | to: node.id,
710 | name: "do",
711 | type: "solid",
712 | arrow: true
713 | }),
714 | bodyEntryNodes
715 | ),
716 | ...fp.map(
717 | node => ({
718 | from: node.id,
719 | to: thisNode.id,
720 | name: "loop",
721 | type: "solid",
722 | arrow: true
723 | }),
724 | bodyExitNodes
725 | )
726 | ];
727 | return {
728 | nodes: [thisNode, ...bodyNodes],
729 | edges: [...thisEdges, ...bodyEdges],
730 | entryNodes: [thisNode],
731 | exitNodes: [...bodyBreakNodes, thisNode],
732 | breakNodes: [],
733 | subGraphs: []
734 | };
735 | }
736 | case "IfStatement": {
737 | const thisNode = cleanGraphNode({
738 | id: makeIdFromAstNode(statement),
739 | name: `if ${generate(statement.test).code}`,
740 | shape: "rhombus"
741 | });
742 | const {
743 | nodes: consequentNodes,
744 | edges: consequentEdges,
745 | entryNodes: consequentEntryNodes,
746 | exitNodes: consequentExitNodes,
747 | breakNodes: consequentBreakNodes
748 | } = transformGeneralAstToGraph(statement.consequent);
749 | const {
750 | nodes: alternateNodes,
751 | edges: alternateEdges,
752 | entryNodes: alternateEntryNodes,
753 | exitNodes: alternateExitNodes,
754 | breakNodes: alternateBreakNodes
755 | } = !fp.isNil(statement.alternate)
756 | ? transformGeneralAstToGraph(statement.alternate)
757 | : {
758 | nodes: [],
759 | edges: [],
760 | entryNodes: [],
761 | exitNodes: [thisNode],
762 | breakNodes: []
763 | };
764 |
765 | const thisEdges = [
766 | ...fp.map(
767 | node => ({
768 | from: thisNode.id,
769 | to: node.id,
770 | name: "true",
771 | type: "solid",
772 | arrow: true
773 | }),
774 | consequentEntryNodes
775 | ),
776 | ...fp.map(
777 | node => ({
778 | from: thisNode.id,
779 | to: node.id,
780 | name: "false",
781 | type: "solid",
782 | arrow: true
783 | }),
784 | alternateEntryNodes
785 | )
786 | ];
787 | return {
788 | nodes: [thisNode, ...consequentNodes, ...alternateNodes],
789 | edges: [...thisEdges, ...consequentEdges, ...alternateEdges],
790 | entryNodes: [thisNode],
791 | exitNodes: [...consequentExitNodes, ...alternateExitNodes],
792 | breakNodes: [...consequentBreakNodes, ...alternateBreakNodes],
793 | subGraphs: []
794 | };
795 | }
796 | case "SwitchStatement":
797 | {
798 | const thisNode = cleanGraphNode({
799 | id: makeIdFromAstNode(statement),
800 | name: `switch ${generate(statement.discriminant).code} `,
801 | shape: "rhombus"
802 | });
803 |
804 | const scopeGraph = fp.reduce(
805 | (
806 | { nodes, edges, entryNodes, exitNodes, breakNodes },
807 | caseAstElement
808 | ) => {
809 | const {
810 | nodes: caseNodes,
811 | edges: caseEdges,
812 | entryNodes: caseEntryNodes,
813 | exitNodes: caseExitNodes,
814 | breakNodes: caseBreakNodes
815 | } = transformGeneralAstToGraph(caseAstElement.consequent);
816 | const caseEntryEdges = fp.map(
817 | node => ({
818 | from: thisNode.id,
819 | to: node.id,
820 | name: fp.isEmpty(caseAstElement.test)
821 | ? "default"
822 | : generate(caseAstElement.test).code,
823 | type: "solid",
824 | arrow: true
825 | }),
826 | caseEntryNodes
827 | );
828 | const caseFollowEdges = fp.flatten(
829 | fp.map(
830 | exitNode =>
831 | fp.map(
832 | entryNode => ({
833 | from: exitNode.id,
834 | to: entryNode.id,
835 | name: "",
836 | type: "solid",
837 | arrow: true
838 | }),
839 | caseEntryNodes
840 | ),
841 | exitNodes
842 | )
843 | );
844 | // console.log("caseNodes", caseNodes);
845 | return {
846 | nodes: [...nodes, ...caseNodes],
847 | edges: fp.compact([
848 | ...edges,
849 | ...caseEdges,
850 | ...caseEntryEdges,
851 | ...caseFollowEdges
852 | ]),
853 | entryNodes: [...entryNodes],
854 | exitNodes: [...caseExitNodes],
855 | breakNodes: [...breakNodes, ...caseBreakNodes]
856 | };
857 | },
858 | {
859 | nodes: [thisNode],
860 | edges: [],
861 | entryNodes: [thisNode],
862 | exitNodes: [],
863 | breakNodes: []
864 | },
865 | statement.cases
866 | );
867 |
868 | return {
869 | nodes: [...scopeGraph.nodes],
870 | edges: [...scopeGraph.edges],
871 | entryNodes: [...scopeGraph.entryNodes],
872 | exitNodes: [...scopeGraph.breakNodes],
873 | breakNodes: [],
874 | subGraphs: []
875 | };
876 | }
877 | FunctionDeclaration;
878 | default:
879 | throw new Error(
880 | `Statements of type ${statement.type} are not yet supported, from ${
881 | statement.loc.start.line
882 | }:${statement.loc.start.column} to ${statement.loc.end.line}:${
883 | statement.loc.end.column
884 | }`
885 | );
886 | }
887 | } catch (error) {
888 | console.log("statement");
889 | console.log(statement);
890 | throw error;
891 | }
892 | }
893 |
894 | function cleanGraphNode({ name, ...rest }) {
895 | return {
896 | ...rest,
897 | name: `"${fp.compose([fp.replace(/(\")/g, "'")])(name)}"`
898 | };
899 | }
900 |
901 | module.exports = { transformGeneralAstToGraph };
902 |
--------------------------------------------------------------------------------