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 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
  ![Open Zeppelin ERC20](./examples/OpenZeppelinERC20.svg)
9
9
 
10
- See [examples](./examples/README.md) for more diagrams.
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
+ ![USDC](./examples/storage/usdc.png)
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 12 or above. [Node 10 is end-of-life 2021-04-30](https://nodejs.org/en/about/releases/) so time to upgrade if you are still running node 10.
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 -h
39
- Usage: sol2uml <fileFolderAddress> [options]
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> only output contracts connected to these comma separated base contract names
52
- -f, --outputFormat <value> output file format: svg, png, sol, dot or all (default: "svg")
53
- -o, --outputFileName <value> output file name
54
- -d, --depthLimit <depth> number of sub folders that will be recursively searched for Solidity files. Default -1 is unlimited (default: -1)
55
- -i, --ignoreFilesOrFolders <filesOrFolders> comma separated list of files or folders to ignore
56
- -n, --network <network> mainnet, ropsten, kovan, rinkeby or goerli (default: "mainnet")
57
- -a, --hideAttributes hide class and interface attributes
58
- -p, --hideOperators hide class and interface operators/functions
59
- -e, --hideEnums hide enum types
60
- -s, --hideStructs hide data structures
61
- -l, --hideLibraries hide libraries
62
- -t, --hideInterfaces hide interfaces
63
- -r, --hideInternals hide private and internal attributes and operators
64
- -k, --etherscanApiKey <key> Etherscan API Key
65
- -c, --clusterFolders cluster contracts into source folders
66
- -v, --verbose run with debugging statements
67
- -h, --help output usage information
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
- [generateFilesFromUmlClasses](./lib/converter.d.ts#L3) is used to write the dot, svg and png files from an array of UML class objects.
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 libraries.
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
+ ![FileLevel-storage](./examples/storage/FileLevel-storage.png)
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,2 @@
1
+ import { Association, UmlClass } from './umlClass';
2
+ export declare const findAssociatedClass: (association: Association, sourceUmlClass: UmlClass, umlClasses: UmlClass[]) => UmlClass;
@@ -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 convertNodeToUmlClass(node: ASTNode, relativePath: string, filesystem?: boolean): UmlClass[];
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.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
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.convertNodeToUmlClass = void 0;
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
- function convertNodeToUmlClass(node, relativePath, filesystem = false) {
28
- let umlClasses = [];
29
- const importedPaths = [];
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
- importedPaths.push(importPath);
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
- importedPaths.push(importPath);
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.importedPaths = importedPaths;
121
+ umlClass.imports = imports;
97
122
  });
98
123
  return umlClasses;
99
124
  }
100
- exports.convertNodeToUmlClass = convertNodeToUmlClass;
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: parseTypeName(member.typeName),
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: parseTypeName(variable.typeName),
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
- let structMembers = [];
227
- subNode.members.forEach((member) => {
228
- structMembers.push({
229
- name: member.name,
230
- type: parseTypeName(member.typeName),
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
- umlClass.structs[subNode.name] = structMembers;
234
- // Recursively parse members for associations
235
- umlClass = addAssociations(subNode.members, umlClass);
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
- let enumValues = [];
239
- subNode.members.forEach((member) => {
240
- enumValues.push(member.name);
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
- umlClass.enums[subNode.name] = enumValues;
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 targetUmlClassName = parseClassName(node.typeName.namePath);
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([node.typeName.valueType], umlClass);
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: umlClass_1.ReferenceType.Memory,
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
- const umlClassName = splitUmlClassName[0];
396
- return umlClassName;
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
- return parseTypeName(typeName.baseTypeName) + '[]';
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 = ((_a = typeName.keyType) === null || _a === void 0 ? void 0 : _a.name) ||
428
- ((_b = typeName.keyType) === null || _b === void 0 ? void 0 : _b.namePath);
429
- const value = parseTypeName(typeName.valueType);
430
- return 'mapping\\(' + key + '=\\>' + value + '\\)';
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: parseTypeName(param.typeName),
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=parser.js.map
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;