sol2uml 1.1.28 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -55
- package/lib/associations.d.ts +2 -0
- package/lib/associations.js +31 -0
- package/lib/{parser.d.ts → converterAST2Classes.d.ts} +1 -1
- package/lib/{parser.js → converterAST2Classes.js} +129 -53
- package/lib/converterClass2Dot.d.ts +13 -0
- package/lib/{dotGenerator.js → converterClass2Dot.js} +30 -60
- package/lib/converterClasses2Dot.d.ts +4 -0
- package/lib/converterClasses2Dot.js +126 -0
- package/lib/converterClasses2Storage.d.ts +29 -0
- package/lib/converterClasses2Storage.js +320 -0
- package/lib/converterStorage2Dot.d.ts +3 -0
- package/lib/converterStorage2Dot.js +89 -0
- package/lib/{contractFilter.d.ts → filterClasses.d.ts} +2 -2
- package/lib/{contractFilter.js → filterClasses.js} +10 -11
- package/lib/index.d.ts +9 -3
- package/lib/index.js +14 -4
- package/lib/{etherscanParser.d.ts → parserEtherscan.d.ts} +15 -6
- package/lib/{etherscanParser.js → parserEtherscan.js} +49 -23
- package/lib/{fileParser.d.ts → parserFiles.d.ts} +2 -2
- package/lib/{fileParser.js → parserFiles.js} +28 -19
- package/lib/parserGeneral.d.ts +5 -0
- package/lib/parserGeneral.js +33 -0
- package/lib/sol2uml.js +118 -79
- package/lib/typeGuards.d.ts +1 -1
- package/lib/typeGuards.js +7 -7
- package/lib/umlClass.d.ts +29 -13
- package/lib/umlClass.js +20 -3
- package/lib/writerFiles.d.ts +7 -0
- package/lib/writerFiles.js +145 -0
- package/package.json +15 -17
- package/lib/converter.d.ts +0 -12
- package/lib/converter.js +0 -227
- package/lib/dotGenerator.d.ts +0 -11
package/README.md
CHANGED
|
@@ -7,12 +7,17 @@
|
|
|
7
7
|
Open Zeppelin's ERC20 token contracts generated from [version 2.5.1](https://github.com/OpenZeppelin/openzeppelin-solidity/tree/v2.5.1/contracts/token/ERC20)
|
|
8
8
|

|
|
9
9
|
|
|
10
|
-
See [
|
|
10
|
+
See more contract diagrams [here](./examples/README.md).
|
|
11
|
+
|
|
12
|
+
USDC storage slots from the [verified source code](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf#code) on Etherscan.
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
See more storage slot diagrams [here](./examples/storage/README.md).
|
|
11
16
|
|
|
12
17
|
# Install
|
|
13
18
|
|
|
14
19
|
The following installation assumes [Node.js](https://nodejs.org/en/download/) has already been installed which comes with [Node Package Manager (NPM)](https://www.npmjs.com/).
|
|
15
|
-
`sol2uml` works with node
|
|
20
|
+
`sol2uml` works with node 14 or above.
|
|
16
21
|
|
|
17
22
|
To install globally so you can run `sol2uml` from anywhere
|
|
18
23
|
```bash
|
|
@@ -33,12 +38,41 @@ npm ls sol2uml -g
|
|
|
33
38
|
|
|
34
39
|
## Command Line Interface (CLI)
|
|
35
40
|
|
|
36
|
-
To see the usage options
|
|
37
41
|
```
|
|
38
|
-
$ sol2uml
|
|
39
|
-
Usage: sol2uml <
|
|
42
|
+
$ sol2uml --help
|
|
43
|
+
Usage: sol2uml [subcommand] <options>
|
|
44
|
+
The three subcommands:
|
|
45
|
+
* class: Generates a UML class diagram from Solidity source code. default
|
|
46
|
+
* storage: Generates a diagram of a contract's storage slots.
|
|
47
|
+
* flatten: Pulls verified source files from a Blockchain explorer into one, flat, local Solidity file.
|
|
48
|
+
|
|
49
|
+
The Solidity code can be pulled from verified source code on Blockchain explorers like Etherscan or from local Solidity files.
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
-sf, --subfolders <value> number of subfolders that will be recursively searched for Solidity files. (default: all)
|
|
53
|
+
-f, --outputFormat <value> output file format. (choices: "svg", "png", "dot", "all", default: "svg")
|
|
54
|
+
-o, --outputFileName <value> output file name
|
|
55
|
+
-i, --ignoreFilesOrFolders <filesOrFolders> comma separated list of files or folders to ignore
|
|
56
|
+
-n, --network <network> Ethereum network (choices: "mainnet", "polygon", "bsc", "arbitrum", "ropsten", "kovan", "rinkeby", "goerli", default: "mainnet")
|
|
57
|
+
-k, --apiKey <key> Etherscan, Polygonscan or BscScan API key
|
|
58
|
+
-v, --verbose run with debugging statements (default: false)
|
|
59
|
+
-h, --help display help for command
|
|
60
|
+
|
|
61
|
+
Commands:
|
|
62
|
+
class [options] [fileFolderAddress] Generates a UML class diagram from Solidity source code.
|
|
63
|
+
storage [options] <fileFolderAddress> output a contracts storage slots
|
|
64
|
+
flatten <contractAddress> get all verified source code for a contract from the Blockchain explorer into one local file
|
|
65
|
+
help [command] display help for command
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Class usage
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
$sol2uml class --help
|
|
72
|
+
Usage: sol2uml class <fileFolderAddress> [options]
|
|
40
73
|
|
|
41
74
|
Generates UML diagrams from Solidity source code.
|
|
75
|
+
|
|
42
76
|
If no file, folder or address is passes as the first argument, the working folder is used.
|
|
43
77
|
When a folder is used, all *.sol files are found in that folder and all sub folders.
|
|
44
78
|
A comma separated list of files and folders can also used. For example
|
|
@@ -47,84 +81,99 @@ A comma separated list of files and folders can also used. For example
|
|
|
47
81
|
If an Ethereum address with a 0x prefix is passed, the verified source code from Etherscan will be used. For example
|
|
48
82
|
sol2uml 0x79fEbF6B9F76853EDBcBc913e6aAE8232cFB9De9
|
|
49
83
|
|
|
84
|
+
Generates a UML class diagram from Solidity source code.
|
|
85
|
+
|
|
86
|
+
Arguments:
|
|
87
|
+
fileFolderAddress file name, base folder or contract address (default: "/Users/nicholasaddison/Documents/workspaces/sol2uml")
|
|
88
|
+
|
|
50
89
|
Options:
|
|
51
|
-
-b, --baseContractNames <value>
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
-b, --baseContractNames <value> only output contracts connected to these comma separated base contract names
|
|
91
|
+
-d, --depth <value> depth of connected classes to the base contracts. 1 will only show directly connected contracts, interfaces, libraries, structs and enums. (default: all)
|
|
92
|
+
-c, --clusterFolders cluster contracts into source folders (default: false)
|
|
93
|
+
-hv, --hideVariables hide variables from contracts, interfaces, structs and enums (default: false)
|
|
94
|
+
-hf, --hideFunctions hide functions from contracts, interfaces and libraries (default: false)
|
|
95
|
+
-hp, --hidePrivates hide private and internal attributes and operators (default: false)
|
|
96
|
+
-he, --hideEnums hide enum types (default: false)
|
|
97
|
+
-hs, --hideStructs hide data structures (default: false)
|
|
98
|
+
-hl, --hideLibraries hide libraries (default: false)
|
|
99
|
+
-hi, --hideInterfaces hide interfaces (default: false)
|
|
100
|
+
-ha, --hideAbstracts hide abstract contracts (default: false)
|
|
101
|
+
-hn, --hideFilename hide relative path and file name (default: false)
|
|
102
|
+
-h, --help display help for command
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Storage usage
|
|
106
|
+
|
|
68
107
|
```
|
|
108
|
+
Usage: sol2uml storage [options] <fileFolderAddress>
|
|
109
|
+
|
|
110
|
+
Visually display a contract's storage slots.
|
|
111
|
+
|
|
112
|
+
WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A known example is storage arrays declared with a constant, immutable or expression will show as only taking one slot but it could be more. Storage arrays declared with an integer work.
|
|
113
|
+
|
|
114
|
+
Arguments:
|
|
115
|
+
fileFolderAddress file name, base folder or contract address
|
|
116
|
+
|
|
117
|
+
Options:
|
|
118
|
+
-c, --contractName <value> Contract name in local Solidity files. Not needed when using an address as the first argument.
|
|
119
|
+
-h, --help display help for command
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Flatten usage
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
$sol2uml flatten --help
|
|
127
|
+
Usage: sol2uml flatten [options] <contractAddress>
|
|
128
|
+
|
|
129
|
+
get all verified source code for a contract from the Blockchain explorer into one local file
|
|
130
|
+
|
|
131
|
+
Arguments:
|
|
132
|
+
contractAddress Contract address
|
|
133
|
+
|
|
134
|
+
Options:
|
|
135
|
+
-h, --help display help for command
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## UML Class diagram examples
|
|
69
140
|
|
|
70
141
|
To generate a diagram of all contracts under the contracts folder and its sub folders
|
|
71
142
|
```bash
|
|
72
|
-
sol2uml ./contracts
|
|
143
|
+
sol2uml class ./contracts
|
|
73
144
|
```
|
|
74
145
|
|
|
75
146
|
To generate a diagram of EtherDelta's contract from the verified source code on [Etherscan](https://etherscan.io/address/0x8d12A197cB00D4747a1fe03395095ce2A5CC6819#code). The output wil be a svg file `0x8d12A197cB00D4747a1fe03395095ce2A5CC6819.svg` in the working folder.
|
|
76
147
|
```bash
|
|
77
|
-
sol2uml 0x8d12A197cB00D4747a1fe03395095ce2A5CC6819
|
|
148
|
+
sol2uml class 0x8d12A197cB00D4747a1fe03395095ce2A5CC6819
|
|
78
149
|
```
|
|
79
150
|
|
|
80
151
|
To generate a diagram of EtherDelta's contract from the verified source code on [Etherscan Ropsten](https://ropsten.etherscan.io/address/0xa19833bd291b66aB0E17b9C6d46D2Ec5fEC15190#code). The output wil be a svg file `0xa19833bd291b66aB0E17b9C6d46D2Ec5fEC15190.svg` in the working folder.
|
|
81
152
|
```bash
|
|
82
|
-
sol2uml 0xa19833bd291b66aB0E17b9C6d46D2Ec5fEC15190 -n ropsten
|
|
153
|
+
sol2uml class 0xa19833bd291b66aB0E17b9C6d46D2Ec5fEC15190 -n ropsten
|
|
83
154
|
```
|
|
84
155
|
|
|
85
156
|
To generate all Solidity files under some root folder and output the svg file to a specific location
|
|
86
157
|
```bash
|
|
87
|
-
sol2uml path/to/contracts/root/folder -o ./outputFile.svg
|
|
158
|
+
sol2uml class path/to/contracts/root/folder -o ./outputFile.svg
|
|
88
159
|
```
|
|
89
160
|
|
|
90
161
|
To generate a diagram of all contracts in a single Solidity file, the output file in png format to output file `./someFile.png`
|
|
91
162
|
```bash
|
|
92
|
-
sol2uml path/to/contracts/root/folder/solidity/file.sol -f png -o ./someFile.png
|
|
163
|
+
sol2uml class path/to/contracts/root/folder/solidity/file.sol -f png -o ./someFile.png
|
|
93
164
|
```
|
|
94
165
|
|
|
95
166
|
To generate a diagram of all Solidity files under the `contracts` and `node_modules/openzeppelin-solidity` folders. The output will be `contracts.svg` and `contracts.png` files in the working folder.
|
|
96
167
|
```bash
|
|
97
|
-
sol2uml ./contracts,node_modules/openzeppelin-solidity -f all -v
|
|
168
|
+
sol2uml class ./contracts,node_modules/openzeppelin-solidity -f all -v
|
|
98
169
|
```
|
|
99
170
|
|
|
100
171
|
To generate a diagram of all Solidity files under the working folder ignoring and files under the `solparse`, `@solidity-parser` and `ethlint` folders, which will be under the `node_modules` folder.
|
|
101
172
|
```bash
|
|
102
|
-
sol2uml -i solparse,@solidity-parser,ethlint
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## Application Programming Interface (API)
|
|
106
|
-
|
|
107
|
-
The main function that parses Solidity source code from files or files in folders is [parseUmlClassesFromFiles](./lib/fileParser.d.ts#L3). This returns an array of UML class objects.
|
|
108
|
-
|
|
109
|
-
[EtherscanParser](./lib/etherscanParser.d.ts#L5) is a class that parses Etherscan's verified Solidity source code for a contract. For example
|
|
110
|
-
```ts
|
|
111
|
-
import { convertUmlClassesToSvg, EtherscanParser } from 'sol2uml'
|
|
112
|
-
|
|
113
|
-
async function generateSvg() {
|
|
114
|
-
const etherscanParser = new EtherscanParser()
|
|
115
|
-
|
|
116
|
-
// get the verified source code from Etherscan for the contract address and
|
|
117
|
-
// parse Solidity into UML class objects
|
|
118
|
-
const umlClasses = await etherscanParser.getUmlClasses('0xf5dce57282a584d2746faf1593d3121fcac444dc')
|
|
119
|
-
|
|
120
|
-
// Convert UML classes to a svg string
|
|
121
|
-
const svg = await convertUmlClassesToSvg(umlClasses)
|
|
122
|
-
}
|
|
173
|
+
sol2uml class -i solparse,@solidity-parser,ethlint
|
|
123
174
|
```
|
|
124
175
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# UML Syntax
|
|
176
|
+
# UML Class Diagram Syntax
|
|
128
177
|
|
|
129
178
|
Good online resources for learning UML
|
|
130
179
|
* [UML 2 Class Diagramming Guidelines](http://www.agilemodeling.com/style/classDiagram.htm)
|
|
@@ -154,9 +203,9 @@ A Solidity variable becomes an attribute in UML and a Solidity function becomes
|
|
|
154
203
|
|
|
155
204
|
Lines:
|
|
156
205
|
- Solid lines for
|
|
157
|
-
- link the contract types of storage (state) variables. This can be linked to contracts, interfaces or
|
|
206
|
+
- link the contract types of storage (state) variables. This can be linked to contracts, interfaces, libraries or file level structs and enums.
|
|
158
207
|
- generalisations of contracts and abstract contracts.
|
|
159
|
-
- aggregated structs and enums
|
|
208
|
+
- aggregated contract level structs and enums.
|
|
160
209
|
- Dashed lines for
|
|
161
210
|
- generalisations of interfaces.
|
|
162
211
|
- types of memory variables.
|
|
@@ -164,7 +213,19 @@ Lines:
|
|
|
164
213
|
Heads/Tails:
|
|
165
214
|
- An empty triangle head for generalisations of contracts, interfaces and abstract contracts.
|
|
166
215
|
- An open arrow head for storage or memory variable dependencies
|
|
167
|
-
- A diamond tail for aggregations of structs and enums
|
|
216
|
+
- A diamond tail for aggregations of contract level structs and enums
|
|
217
|
+
|
|
218
|
+
## Storage diagram
|
|
219
|
+
|
|
220
|
+

|
|
221
|
+
|
|
222
|
+
See more storage slot diagrams [here](./examples/storage/README.md).
|
|
223
|
+
|
|
224
|
+
# Version 2.x changes
|
|
225
|
+
|
|
226
|
+
The biggest change with 2.x is the introduction of subcommands as sol2uml can now draw contract storage diagrams.
|
|
227
|
+
|
|
228
|
+
See [version 2.x](./version2.md) for a list of changes from 1.x.
|
|
168
229
|
|
|
169
230
|
# Contribution
|
|
170
231
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findAssociatedClass = void 0;
|
|
4
|
+
// Find the UML class linked to the association
|
|
5
|
+
const findAssociatedClass = (association, sourceUmlClass, umlClasses) => {
|
|
6
|
+
return umlClasses.find((targetUmlClass) => {
|
|
7
|
+
return (
|
|
8
|
+
// class is in the same source file
|
|
9
|
+
(association.targetUmlClassName === targetUmlClass.name &&
|
|
10
|
+
sourceUmlClass.absolutePath === targetUmlClass.absolutePath) ||
|
|
11
|
+
// imported classes with no explicit import names
|
|
12
|
+
(association.targetUmlClassName === targetUmlClass.name &&
|
|
13
|
+
sourceUmlClass.imports.find((i) => i.absolutePath === targetUmlClass.absolutePath &&
|
|
14
|
+
i.classNames.length === 0)) ||
|
|
15
|
+
// imported classes with explicit import names or import aliases
|
|
16
|
+
sourceUmlClass.imports.find((i) => i.absolutePath === targetUmlClass.absolutePath &&
|
|
17
|
+
i.classNames.find((importedClass) =>
|
|
18
|
+
// no import alias
|
|
19
|
+
(association.targetUmlClassName ===
|
|
20
|
+
importedClass.className &&
|
|
21
|
+
importedClass.className ===
|
|
22
|
+
targetUmlClass.name &&
|
|
23
|
+
importedClass.alias == undefined) ||
|
|
24
|
+
// import alias
|
|
25
|
+
(association.targetUmlClassName ===
|
|
26
|
+
importedClass.alias &&
|
|
27
|
+
importedClass.className === targetUmlClass.name))));
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
exports.findAssociatedClass = findAssociatedClass;
|
|
31
|
+
//# sourceMappingURL=associations.js.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ASTNode } from '@solidity-parser/parser/dist/src/ast-types';
|
|
2
2
|
import { UmlClass } from './umlClass';
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function convertAST2UmlClasses(node: ASTNode, relativePath: string, filesystem?: boolean): UmlClass[];
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -19,14 +23,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
19
23
|
return result;
|
|
20
24
|
};
|
|
21
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.
|
|
26
|
+
exports.convertAST2UmlClasses = void 0;
|
|
23
27
|
const path = __importStar(require("path"));
|
|
24
28
|
const umlClass_1 = require("./umlClass");
|
|
25
29
|
const typeGuards_1 = require("./typeGuards");
|
|
26
30
|
const debug = require('debug')('sol2uml');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
31
|
+
let umlClasses = [];
|
|
32
|
+
function convertAST2UmlClasses(node, relativePath, filesystem = false) {
|
|
33
|
+
const imports = [];
|
|
34
|
+
umlClasses = [];
|
|
30
35
|
if (node.type === 'SourceUnit') {
|
|
31
36
|
node.children.forEach((childNode) => {
|
|
32
37
|
if (childNode.type === 'ContractDefinition') {
|
|
@@ -75,7 +80,17 @@ function convertNodeToUmlClass(node, relativePath, filesystem = false) {
|
|
|
75
80
|
const importPath = require.resolve(childNode.path, {
|
|
76
81
|
paths: [codeFolder],
|
|
77
82
|
});
|
|
78
|
-
|
|
83
|
+
imports.push({
|
|
84
|
+
absolutePath: importPath,
|
|
85
|
+
classNames: childNode.symbolAliases
|
|
86
|
+
? childNode.symbolAliases.map((alias) => {
|
|
87
|
+
return {
|
|
88
|
+
className: alias[0],
|
|
89
|
+
alias: alias[1],
|
|
90
|
+
};
|
|
91
|
+
})
|
|
92
|
+
: [],
|
|
93
|
+
});
|
|
79
94
|
}
|
|
80
95
|
catch (err) {
|
|
81
96
|
debug(`Failed to resolve import ${childNode.path} from file ${relativePath}`);
|
|
@@ -84,7 +99,17 @@ function convertNodeToUmlClass(node, relativePath, filesystem = false) {
|
|
|
84
99
|
else {
|
|
85
100
|
// this has come from Etherscan
|
|
86
101
|
const importPath = path.join(codeFolder, childNode.path);
|
|
87
|
-
|
|
102
|
+
imports.push({
|
|
103
|
+
absolutePath: importPath,
|
|
104
|
+
classNames: childNode.symbolAliases
|
|
105
|
+
? childNode.symbolAliases.map((alias) => {
|
|
106
|
+
return {
|
|
107
|
+
className: alias[0],
|
|
108
|
+
alias: alias[1],
|
|
109
|
+
};
|
|
110
|
+
})
|
|
111
|
+
: [],
|
|
112
|
+
});
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
115
|
});
|
|
@@ -93,16 +118,18 @@ function convertNodeToUmlClass(node, relativePath, filesystem = false) {
|
|
|
93
118
|
throw new Error(`AST node not of type SourceUnit`);
|
|
94
119
|
}
|
|
95
120
|
umlClasses.forEach((umlClass) => {
|
|
96
|
-
umlClass.
|
|
121
|
+
umlClass.imports = imports;
|
|
97
122
|
});
|
|
98
123
|
return umlClasses;
|
|
99
124
|
}
|
|
100
|
-
exports.
|
|
125
|
+
exports.convertAST2UmlClasses = convertAST2UmlClasses;
|
|
101
126
|
function parseStructDefinition(umlClass, node) {
|
|
102
127
|
node.members.forEach((member) => {
|
|
128
|
+
const [type, attributeType] = parseTypeName(member.typeName);
|
|
103
129
|
umlClass.attributes.push({
|
|
104
130
|
name: member.name,
|
|
105
|
-
type
|
|
131
|
+
type,
|
|
132
|
+
attributeType,
|
|
106
133
|
});
|
|
107
134
|
});
|
|
108
135
|
// Recursively parse struct members for associations
|
|
@@ -134,25 +161,29 @@ function parseContractDefinition(umlClass, node) {
|
|
|
134
161
|
});
|
|
135
162
|
// For each sub node
|
|
136
163
|
node.subNodes.forEach((subNode) => {
|
|
137
|
-
if (typeGuards_1.isStateVariableDeclaration(subNode)) {
|
|
164
|
+
if ((0, typeGuards_1.isStateVariableDeclaration)(subNode)) {
|
|
138
165
|
subNode.variables.forEach((variable) => {
|
|
166
|
+
const [type, attributeType] = parseTypeName(variable.typeName);
|
|
167
|
+
const valueStore = variable.isDeclaredConst || variable.isImmutable;
|
|
139
168
|
umlClass.attributes.push({
|
|
140
169
|
visibility: parseVisibility(variable.visibility),
|
|
141
170
|
name: variable.name,
|
|
142
|
-
type
|
|
171
|
+
type,
|
|
172
|
+
attributeType,
|
|
173
|
+
compiled: valueStore,
|
|
143
174
|
});
|
|
144
175
|
});
|
|
145
176
|
// Recursively parse variables for associations
|
|
146
177
|
umlClass = addAssociations(subNode.variables, umlClass);
|
|
147
178
|
}
|
|
148
|
-
else if (typeGuards_1.isUsingForDeclaration(subNode)) {
|
|
179
|
+
else if ((0, typeGuards_1.isUsingForDeclaration)(subNode)) {
|
|
149
180
|
// Add association to library contract
|
|
150
181
|
umlClass.addAssociation({
|
|
151
182
|
referenceType: umlClass_1.ReferenceType.Memory,
|
|
152
183
|
targetUmlClassName: subNode.libraryName,
|
|
153
184
|
});
|
|
154
185
|
}
|
|
155
|
-
else if (typeGuards_1.isFunctionDefinition(subNode)) {
|
|
186
|
+
else if ((0, typeGuards_1.isFunctionDefinition)(subNode)) {
|
|
156
187
|
if (subNode.isConstructor) {
|
|
157
188
|
umlClass.operators.push({
|
|
158
189
|
name: 'constructor',
|
|
@@ -202,7 +233,7 @@ function parseContractDefinition(umlClass, node) {
|
|
|
202
233
|
umlClass = addAssociations(subNode.body.statements, umlClass);
|
|
203
234
|
}
|
|
204
235
|
}
|
|
205
|
-
else if (typeGuards_1.isModifierDefinition(subNode)) {
|
|
236
|
+
else if ((0, typeGuards_1.isModifierDefinition)(subNode)) {
|
|
206
237
|
umlClass.operators.push({
|
|
207
238
|
stereotype: umlClass_1.OperatorStereotype.Modifier,
|
|
208
239
|
name: subNode.name,
|
|
@@ -213,7 +244,7 @@ function parseContractDefinition(umlClass, node) {
|
|
|
213
244
|
umlClass = addAssociations(subNode.body.statements, umlClass);
|
|
214
245
|
}
|
|
215
246
|
}
|
|
216
|
-
else if (typeGuards_1.isEventDefinition(subNode)) {
|
|
247
|
+
else if ((0, typeGuards_1.isEventDefinition)(subNode)) {
|
|
217
248
|
umlClass.operators.push({
|
|
218
249
|
stereotype: umlClass_1.OperatorStereotype.Event,
|
|
219
250
|
name: subNode.name,
|
|
@@ -222,24 +253,29 @@ function parseContractDefinition(umlClass, node) {
|
|
|
222
253
|
// Recursively parse event parameters for associations
|
|
223
254
|
umlClass = addAssociations(subNode.parameters, umlClass);
|
|
224
255
|
}
|
|
225
|
-
else if (typeGuards_1.isStructDefinition(subNode)) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
});
|
|
256
|
+
else if ((0, typeGuards_1.isStructDefinition)(subNode)) {
|
|
257
|
+
const structClass = new umlClass_1.UmlClass({
|
|
258
|
+
name: subNode.name,
|
|
259
|
+
absolutePath: umlClass.absolutePath,
|
|
260
|
+
relativePath: umlClass.relativePath,
|
|
261
|
+
stereotype: umlClass_1.ClassStereotype.Struct,
|
|
232
262
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
263
|
+
parseStructDefinition(structClass, subNode);
|
|
264
|
+
umlClasses.push(structClass);
|
|
265
|
+
// list as contract level struct
|
|
266
|
+
umlClass.structs.push(structClass.id);
|
|
236
267
|
}
|
|
237
|
-
else if (typeGuards_1.isEnumDefinition(subNode)) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
268
|
+
else if ((0, typeGuards_1.isEnumDefinition)(subNode)) {
|
|
269
|
+
const enumClass = new umlClass_1.UmlClass({
|
|
270
|
+
name: subNode.name,
|
|
271
|
+
absolutePath: umlClass.absolutePath,
|
|
272
|
+
relativePath: umlClass.relativePath,
|
|
273
|
+
stereotype: umlClass_1.ClassStereotype.Enum,
|
|
241
274
|
});
|
|
242
|
-
|
|
275
|
+
parseEnumDefinition(enumClass, subNode);
|
|
276
|
+
umlClasses.push(enumClass);
|
|
277
|
+
// list as contract level enum
|
|
278
|
+
umlClass.enums.push(enumClass.id);
|
|
243
279
|
}
|
|
244
280
|
});
|
|
245
281
|
return umlClass;
|
|
@@ -255,6 +291,10 @@ function addAssociations(nodes, umlClass) {
|
|
|
255
291
|
if (node === null) {
|
|
256
292
|
break;
|
|
257
293
|
}
|
|
294
|
+
// If state variable then mark as a Storage reference, else Memory
|
|
295
|
+
const referenceType = node.isStateVar
|
|
296
|
+
? umlClass_1.ReferenceType.Storage
|
|
297
|
+
: umlClass_1.ReferenceType.Memory;
|
|
258
298
|
// Recursively parse sub nodes that can has variable declarations
|
|
259
299
|
switch (node.type) {
|
|
260
300
|
case 'VariableDeclaration':
|
|
@@ -262,25 +302,41 @@ function addAssociations(nodes, umlClass) {
|
|
|
262
302
|
break;
|
|
263
303
|
}
|
|
264
304
|
if (node.typeName.type === 'UserDefinedTypeName') {
|
|
265
|
-
// If state variable then mark as a Storage reference, else Memory
|
|
266
|
-
const referenceType = node.isStateVar
|
|
267
|
-
? umlClass_1.ReferenceType.Storage
|
|
268
|
-
: umlClass_1.ReferenceType.Memory;
|
|
269
305
|
// Library references can have a Library dot variable notation. eg Set.Data
|
|
270
|
-
const
|
|
306
|
+
const { umlClassName, structOrEnum } = parseClassName(node.typeName.namePath);
|
|
271
307
|
umlClass.addAssociation({
|
|
272
308
|
referenceType,
|
|
273
|
-
targetUmlClassName,
|
|
309
|
+
targetUmlClassName: umlClassName,
|
|
274
310
|
});
|
|
311
|
+
if (structOrEnum) {
|
|
312
|
+
umlClass.addAssociation({
|
|
313
|
+
referenceType,
|
|
314
|
+
targetUmlClassName: structOrEnum,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
275
317
|
}
|
|
276
318
|
else if (node.typeName.type === 'Mapping') {
|
|
277
319
|
umlClass = addAssociations([node.typeName.keyType], umlClass);
|
|
278
|
-
umlClass = addAssociations([
|
|
320
|
+
umlClass = addAssociations([
|
|
321
|
+
{
|
|
322
|
+
...node.typeName.valueType,
|
|
323
|
+
isStateVar: node.isStateVar,
|
|
324
|
+
},
|
|
325
|
+
], umlClass);
|
|
326
|
+
// Array of user defined types
|
|
327
|
+
}
|
|
328
|
+
else if (node.typeName.type == 'ArrayTypeName' &&
|
|
329
|
+
node.typeName.baseTypeName.type === 'UserDefinedTypeName') {
|
|
330
|
+
const { umlClassName } = parseClassName(node.typeName.baseTypeName.namePath);
|
|
331
|
+
umlClass.addAssociation({
|
|
332
|
+
referenceType,
|
|
333
|
+
targetUmlClassName: umlClassName,
|
|
334
|
+
});
|
|
279
335
|
}
|
|
280
336
|
break;
|
|
281
337
|
case 'UserDefinedTypeName':
|
|
282
338
|
umlClass.addAssociation({
|
|
283
|
-
referenceType:
|
|
339
|
+
referenceType: referenceType,
|
|
284
340
|
targetUmlClassName: node.namePath,
|
|
285
341
|
});
|
|
286
342
|
break;
|
|
@@ -388,12 +444,17 @@ function parseClassName(rawClassName) {
|
|
|
388
444
|
if (!rawClassName ||
|
|
389
445
|
typeof rawClassName !== 'string' ||
|
|
390
446
|
rawClassName.length === 0) {
|
|
391
|
-
return
|
|
447
|
+
return {
|
|
448
|
+
umlClassName: '',
|
|
449
|
+
structOrEnum: rawClassName,
|
|
450
|
+
};
|
|
392
451
|
}
|
|
393
452
|
// Split the name on dot
|
|
394
453
|
const splitUmlClassName = rawClassName.split('.');
|
|
395
|
-
|
|
396
|
-
|
|
454
|
+
return {
|
|
455
|
+
umlClassName: splitUmlClassName[0],
|
|
456
|
+
structOrEnum: splitUmlClassName[1],
|
|
457
|
+
};
|
|
397
458
|
}
|
|
398
459
|
function parseVisibility(visibility) {
|
|
399
460
|
switch (visibility) {
|
|
@@ -412,22 +473,36 @@ function parseVisibility(visibility) {
|
|
|
412
473
|
}
|
|
413
474
|
}
|
|
414
475
|
function parseTypeName(typeName) {
|
|
415
|
-
var _a, _b;
|
|
416
476
|
switch (typeName.type) {
|
|
417
477
|
case 'ElementaryTypeName':
|
|
418
|
-
return typeName.name;
|
|
478
|
+
return [typeName.name, umlClass_1.AttributeType.Elementary];
|
|
419
479
|
case 'UserDefinedTypeName':
|
|
420
|
-
return typeName.namePath;
|
|
480
|
+
return [typeName.namePath, umlClass_1.AttributeType.UserDefined];
|
|
421
481
|
case 'FunctionTypeName':
|
|
422
482
|
// TODO add params and return type
|
|
423
|
-
return typeName.type + '\\(\\)';
|
|
483
|
+
return [typeName.type + '\\(\\)', umlClass_1.AttributeType.Function];
|
|
424
484
|
case 'ArrayTypeName':
|
|
425
|
-
|
|
485
|
+
const [arrayElementType] = parseTypeName(typeName.baseTypeName);
|
|
486
|
+
let length = '';
|
|
487
|
+
if (Number.isInteger(typeName.length)) {
|
|
488
|
+
length = typeName.length.toString();
|
|
489
|
+
}
|
|
490
|
+
else if (typeName.length?.type === 'NumberLiteral') {
|
|
491
|
+
length = typeName.length.number;
|
|
492
|
+
}
|
|
493
|
+
else if (typeName.length?.type === 'Identifier') {
|
|
494
|
+
length = typeName.length.name;
|
|
495
|
+
}
|
|
496
|
+
// TODO does not currently handle Expression types like BinaryOperation
|
|
497
|
+
return [arrayElementType + '[' + length + ']', umlClass_1.AttributeType.Array];
|
|
426
498
|
case 'Mapping':
|
|
427
|
-
const key =
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
return
|
|
499
|
+
const key = typeName.keyType?.name ||
|
|
500
|
+
typeName.keyType?.namePath;
|
|
501
|
+
const [valueType] = parseTypeName(typeName.valueType);
|
|
502
|
+
return [
|
|
503
|
+
'mapping\\(' + key + '=\\>' + valueType + '\\)',
|
|
504
|
+
umlClass_1.AttributeType.Mapping,
|
|
505
|
+
];
|
|
431
506
|
default:
|
|
432
507
|
throw Error(`Invalid typeName ${typeName}`);
|
|
433
508
|
}
|
|
@@ -438,9 +513,10 @@ function parseParameters(params) {
|
|
|
438
513
|
}
|
|
439
514
|
let parameters = [];
|
|
440
515
|
for (const param of params) {
|
|
516
|
+
const [type] = parseTypeName(param.typeName);
|
|
441
517
|
parameters.push({
|
|
442
518
|
name: param.name,
|
|
443
|
-
type
|
|
519
|
+
type,
|
|
444
520
|
});
|
|
445
521
|
}
|
|
446
522
|
return parameters;
|
|
@@ -462,4 +538,4 @@ function parseContractKind(kind) {
|
|
|
462
538
|
function parsePayable(stateMutability) {
|
|
463
539
|
return stateMutability === 'payable';
|
|
464
540
|
}
|
|
465
|
-
//# sourceMappingURL=
|
|
541
|
+
//# sourceMappingURL=converterAST2Classes.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { UmlClass } from './umlClass';
|
|
2
|
+
export interface ClassOptions {
|
|
3
|
+
hideVariables?: boolean;
|
|
4
|
+
hideFunctions?: boolean;
|
|
5
|
+
hideStructs?: boolean;
|
|
6
|
+
hideEnums?: boolean;
|
|
7
|
+
hideLibraries?: boolean;
|
|
8
|
+
hideInterfaces?: boolean;
|
|
9
|
+
hidePrivates?: boolean;
|
|
10
|
+
hideAbstracts?: boolean;
|
|
11
|
+
hideFilename?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const convertClass2Dot: (umlClass: UmlClass, options?: ClassOptions) => string;
|