sol2uml 2.4.2 → 2.5.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/lib/slotValues.js CHANGED
@@ -3,49 +3,286 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getStorageValues = exports.getStorageValue = void 0;
6
+ exports.escapeString = exports.convert2String = exports.dynamicSlotSize = exports.getSlotValue = exports.getSlotValues = exports.parseValue = exports.addSlotValues = void 0;
7
7
  const bignumber_1 = require("@ethersproject/bignumber");
8
8
  const axios_1 = __importDefault(require("axios"));
9
+ const umlClass_1 = require("./umlClass");
10
+ const utils_1 = require("ethers/lib/utils");
11
+ const SlotValueCache_1 = require("./SlotValueCache");
9
12
  const debug = require('debug')('sol2uml');
10
- const getStorageValue = async (url, contractAddress, slot, blockTag = 'latest') => {
11
- debug(`About to get storage slot ${slot} value for ${contractAddress}`);
12
- const values = await (0, exports.getStorageValues)(url, contractAddress, [slot], blockTag);
13
- debug(`Got slot ${slot} value: ${values[0]}`);
14
- return values[0];
13
+ /**
14
+ * Adds the slot values to the variables in the storage section.
15
+ * This can be rerun for a section as it will only get if the slot value
16
+ * does not exist.
17
+ * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy
18
+ * @param contractAddress Contract address to get the storage slot values from.
19
+ * If contract is proxied, use proxy and not the implementation contract.
20
+ * @param storageSection is mutated with the slot values added to the variables
21
+ * @param arrayItems the number of items to display at the start and end of an array
22
+ * @param blockTag block number or `latest`
23
+ */
24
+ const addSlotValues = async (url, contractAddress, storageSection, arrayItems, blockTag) => {
25
+ const valueVariables = storageSection.variables.filter((variable) => variable.getValue && !variable.slotValue);
26
+ if (valueVariables.length === 0)
27
+ return;
28
+ // for each variable, add all the slots used by the variable.
29
+ const slots = [];
30
+ valueVariables.forEach((variable) => {
31
+ for (let i = 0; variable.fromSlot + i <= variable.toSlot; i++) {
32
+ if (variable.attributeType === umlClass_1.AttributeType.Array &&
33
+ i >= arrayItems &&
34
+ i < variable.toSlot - arrayItems) {
35
+ continue;
36
+ }
37
+ slots.push(variable.fromSlot + i);
38
+ }
39
+ });
40
+ // remove duplicate slot numbers
41
+ const uniqueFromSlots = [...new Set(slots)];
42
+ // Convert slot numbers to BigNumbers and offset dynamic arrays
43
+ let slotKeys = uniqueFromSlots.map((fromSlot) => {
44
+ if (storageSection.offset) {
45
+ return bignumber_1.BigNumber.from(storageSection.offset).add(fromSlot);
46
+ }
47
+ return bignumber_1.BigNumber.from(fromSlot);
48
+ });
49
+ // Get the contract slot values from the node provider
50
+ const values = await (0, exports.getSlotValues)(url, contractAddress, slotKeys, blockTag);
51
+ // For each slot value retrieved
52
+ values.forEach((value, i) => {
53
+ // Get the corresponding slot number for the slot value
54
+ const fromSlot = uniqueFromSlots[i];
55
+ // For each variable in the storage section
56
+ for (const variable of storageSection.variables) {
57
+ if (variable.getValue && variable.fromSlot === fromSlot) {
58
+ debug(`Set slot value ${value} for section "${storageSection.name}", var type ${variable.type}, slot ${variable.fromSlot} offset ${storageSection.offset}`);
59
+ variable.slotValue = value;
60
+ // parse variable value from slot data
61
+ if (variable.displayValue) {
62
+ variable.parsedValue = (0, exports.parseValue)(variable);
63
+ }
64
+ }
65
+ // if variable is past the slot that has the value
66
+ else if (variable.toSlot > fromSlot) {
67
+ break;
68
+ }
69
+ }
70
+ });
71
+ };
72
+ exports.addSlotValues = addSlotValues;
73
+ const parseValue = (variable) => {
74
+ if (!variable.slotValue)
75
+ return undefined;
76
+ const start = 66 - (variable.byteOffset + variable.byteSize) * 2;
77
+ const end = 66 - variable.byteOffset * 2;
78
+ const variableValue = variable.slotValue.substring(start, end);
79
+ try {
80
+ // Contracts, structs and enums
81
+ if (variable.attributeType === umlClass_1.AttributeType.UserDefined) {
82
+ return parseUserDefinedValue(variable, variableValue);
83
+ }
84
+ if (variable.attributeType === umlClass_1.AttributeType.Elementary)
85
+ return parseElementaryValue(variable, variableValue);
86
+ // dynamic arrays
87
+ if (variable.attributeType === umlClass_1.AttributeType.Array &&
88
+ variable.dynamic) {
89
+ return (0, utils_1.formatUnits)('0x' + variableValue, 0);
90
+ }
91
+ return undefined;
92
+ }
93
+ catch (err) {
94
+ throw Error(`Failed to parse variable ${variable.name} of type ${variable.type}, value "${variableValue}"`, { cause: err });
95
+ }
96
+ };
97
+ exports.parseValue = parseValue;
98
+ const parseUserDefinedValue = (variable, variableValue) => {
99
+ // TODO need to handle User Defined Value Types introduced in Solidity
100
+ // https://docs.soliditylang.org/en/v0.8.18/types.html#user-defined-value-types
101
+ // https://blog.soliditylang.org/2021/09/27/user-defined-value-types/
102
+ // using byteSize is crude and will be incorrect for aliases types like int160 or uint160
103
+ if (variable.byteSize === 20) {
104
+ return (0, utils_1.getAddress)('0x' + variableValue);
105
+ }
106
+ // this will also be wrong if the alias is to a 1 byte type. eg bytes1, int8 or uint8
107
+ if (variable.byteSize === 1) {
108
+ // assume 1 byte is an enum so convert value to enum index number
109
+ const index = bignumber_1.BigNumber.from('0x' + variableValue).toNumber();
110
+ // lookup enum value if its available
111
+ return variable?.enumValues ? variable?.enumValues[index] : undefined;
112
+ }
113
+ // we don't parse if a struct which has a size of 32 bytes
114
+ return undefined;
115
+ };
116
+ const parseElementaryValue = (variable, variableValue) => {
117
+ // Elementary types
118
+ if (variable.type === 'bool') {
119
+ if (variableValue === '00')
120
+ return 'false';
121
+ if (variableValue === '01')
122
+ return 'true';
123
+ throw Error(`Failed to parse bool variable "${variable.name}" in slot ${variable.fromSlot}, offset ${variable.byteOffset} and slot value "${variableValue}"`);
124
+ }
125
+ if (variable.type === 'string' || variable.type === 'bytes') {
126
+ if (variable.dynamic) {
127
+ const lastByte = variable.slotValue.slice(-2);
128
+ const size = bignumber_1.BigNumber.from('0x' + lastByte);
129
+ // Check if the last bit is set by AND the size with 0x01
130
+ if (size.and(1).eq(1)) {
131
+ // Return the number of chars or bytes
132
+ return bignumber_1.BigNumber.from(variable.slotValue)
133
+ .sub(1)
134
+ .div(2)
135
+ .toString();
136
+ }
137
+ // The last byte holds the length of the string or bytes in the slot
138
+ const valueHex = '0x' + variableValue.slice(0, size.toNumber());
139
+ if (variable.type === 'bytes')
140
+ return valueHex;
141
+ return `\\"${(0, exports.convert2String)(valueHex)}\\"`;
142
+ }
143
+ if (variable.type === 'bytes')
144
+ return '0x' + variableValue;
145
+ return `\\"${(0, exports.convert2String)('0x' + variableValue)}\\"`;
146
+ }
147
+ if (variable.type === 'address') {
148
+ return (0, utils_1.getAddress)('0x' + variableValue);
149
+ }
150
+ if (variable.type.match(/^uint([0-9]*)$/)) {
151
+ const parsedValue = (0, utils_1.formatUnits)('0x' + variableValue, 0);
152
+ return (0, utils_1.commify)(parsedValue);
153
+ }
154
+ if (variable.type.match(/^bytes([0-9]+)$/)) {
155
+ return '0x' + variableValue;
156
+ }
157
+ if (variable.type.match(/^int([0-9]*)/)) {
158
+ // parse variable value as an unsigned number
159
+ let rawValue = bignumber_1.BigNumber.from('0x' + variableValue);
160
+ // parse the number of bits
161
+ const result = variable.type.match(/^int([0-9]*$)/);
162
+ const bitSize = result[1] ? result[1] : 256;
163
+ // Convert the number of bits to the number of hex characters
164
+ const hexSize = bignumber_1.BigNumber.from(bitSize).div(4).toNumber();
165
+ // bit mask has a leading 1 and the rest 0. 0x8 = 1000 binary
166
+ const mask = '0x80' + '0'.repeat(hexSize - 2);
167
+ // is the first bit a 1?
168
+ const negative = rawValue.and(mask);
169
+ if (negative.gt(0)) {
170
+ // Convert unsigned number to a signed negative
171
+ const negativeOne = '0xFF' + 'F'.repeat(hexSize - 2);
172
+ rawValue = bignumber_1.BigNumber.from(negativeOne).sub(rawValue).add(1).mul(-1);
173
+ }
174
+ const parsedValue = (0, utils_1.formatUnits)(rawValue, 0);
175
+ return (0, utils_1.commify)(parsedValue);
176
+ }
177
+ // add fixed point numbers when they are supported by Solidity
178
+ return undefined;
15
179
  };
16
- exports.getStorageValue = getStorageValue;
17
180
  let jsonRpcId = 0;
18
- const getStorageValues = async (url, contractAddress, slots, blockTag = 'latest') => {
181
+ /**
182
+ * Get storage slot values from JSON-RPC API provider.
183
+ * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy
184
+ * @param contractAddress Contract address to get the storage slot values from.
185
+ * If proxied, use proxy and not the implementation contract.
186
+ * @param slotKeys array of 32 byte slot keys as BigNumbers.
187
+ * @param blockTag block number or `latest`
188
+ * @return slotValues array of 32 byte slot values as hexadecimal strings
189
+ */
190
+ const getSlotValues = async (url, contractAddress, slotKeys, blockTag = 'latest') => {
19
191
  try {
20
- debug(`About to get ${slots.length} storage values for ${contractAddress} at block ${blockTag}`);
192
+ if (slotKeys.length === 0) {
193
+ return [];
194
+ }
21
195
  const block = blockTag === 'latest'
22
196
  ? blockTag
23
- : bignumber_1.BigNumber.from(blockTag).toHexString();
24
- const payload = slots.map((slot) => ({
197
+ : (0, utils_1.hexValue)(bignumber_1.BigNumber.from(blockTag));
198
+ // get cached values and missing slot keys from from cache
199
+ const { cachedValues, missingKeys } = SlotValueCache_1.SlotValueCache.readSlotValues(slotKeys);
200
+ // If all values are in the cache then just return the cached values
201
+ if (missingKeys.length === 0) {
202
+ return cachedValues;
203
+ }
204
+ debug(`About to get ${slotKeys.length} storage values for ${contractAddress} at block ${blockTag} from slot ${missingKeys[0].toString()}`);
205
+ // Get the values for the missing slot keys
206
+ const payload = missingKeys.map((key) => ({
25
207
  id: (jsonRpcId++).toString(),
26
208
  jsonrpc: '2.0',
27
209
  method: 'eth_getStorageAt',
28
- params: [
29
- contractAddress,
30
- bignumber_1.BigNumber.from(slot).toHexString(),
31
- block,
32
- ],
210
+ params: [contractAddress, key, block],
33
211
  }));
34
212
  const response = await axios_1.default.post(url, payload);
35
- console.log(response.data);
36
213
  if (response.data?.error?.message) {
37
- throw new Error(response.data.error.message);
214
+ throw Error(response.data.error.message);
38
215
  }
39
- if (response.data.length !== slots.length) {
40
- throw new Error(`Requested ${slots.length} storage slot values but only got ${response.data.length}`);
216
+ if (response.data.length !== missingKeys.length) {
217
+ throw Error(`Requested ${missingKeys.length} storage slot values but only got ${response.data.length}`);
41
218
  }
42
219
  const responseData = response.data;
43
220
  const sortedResponses = responseData.sort((a, b) => bignumber_1.BigNumber.from(a.id).gt(b.id) ? 1 : -1);
44
- return sortedResponses.map((data) => '0x' + data.result.toUpperCase().slice(2));
221
+ const missingValues = sortedResponses.map((data) => {
222
+ if (data.error) {
223
+ throw Error(`json rpc call with id ${data.id} failed to get storage values: ${data.error?.message}`);
224
+ }
225
+ return '0x' + data.result.toUpperCase().slice(2);
226
+ });
227
+ // add new values to the cache and return the merged slot values
228
+ return SlotValueCache_1.SlotValueCache.addSlotValues(slotKeys, missingKeys, missingValues);
45
229
  }
46
230
  catch (err) {
47
- throw new Error(`Failed to get ${slots.length} storage values for ${contractAddress} from ${url}`, { cause: err });
231
+ throw Error(`Failed to get ${slotKeys.length} storage values for contract ${contractAddress} from ${url}`, { cause: err });
48
232
  }
49
233
  };
50
- exports.getStorageValues = getStorageValues;
234
+ exports.getSlotValues = getSlotValues;
235
+ /**
236
+ * Get storage slot values from JSON-RPC API provider.
237
+ * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy
238
+ * @param contractAddress Contract address to get the storage slot values from.
239
+ * If proxied, use proxy and not the implementation contract.
240
+ * @param slotKey 32 byte slot key as a BigNumber.
241
+ * @param blockTag block number or `latest`
242
+ * @return slotValue 32 byte slot value as hexadecimal string
243
+ */
244
+ const getSlotValue = async (url, contractAddress, slotKey, blockTag) => {
245
+ debug(`About to get storage slot ${slotKey} value for ${contractAddress}`);
246
+ const values = await (0, exports.getSlotValues)(url, contractAddress, [slotKey], blockTag);
247
+ return values[0];
248
+ };
249
+ exports.getSlotValue = getSlotValue;
250
+ /**
251
+ * Calculates the number of string characters or bytes of a string or bytes type.
252
+ * See the following for how string and bytes are stored in storage slots
253
+ * https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#bytes-and-string
254
+ * @param variable the variable with the slotValue that is being sized
255
+ * @return bytes the number of bytes of the dynamic slot. If static, zero is return.
256
+ */
257
+ const dynamicSlotSize = (variable) => {
258
+ try {
259
+ if (!variable?.slotValue)
260
+ throw Error(`Missing slot value.`);
261
+ const last4bits = '0x' + variable.slotValue.slice(-1);
262
+ const last4bitsNum = bignumber_1.BigNumber.from(last4bits).toNumber();
263
+ // If the last 4 bits is an even number then it's not a dynamic slot
264
+ if (last4bitsNum % 2 === 0)
265
+ return 0;
266
+ const sizeRaw = bignumber_1.BigNumber.from(variable.slotValue).toNumber();
267
+ // Adjust the size to bytes
268
+ return (sizeRaw - 1) / 2;
269
+ }
270
+ catch (err) {
271
+ throw Error(`Failed to calculate dynamic slot size for variable "${variable?.name}" of type "${variable?.type}" with slot value ${variable?.slotValue}`, { cause: err });
272
+ }
273
+ };
274
+ exports.dynamicSlotSize = dynamicSlotSize;
275
+ const convert2String = (bytes) => {
276
+ if (bytes ===
277
+ '0x0000000000000000000000000000000000000000000000000000000000000000') {
278
+ return '';
279
+ }
280
+ const rawString = (0, utils_1.toUtf8String)(bytes);
281
+ return (0, exports.escapeString)(rawString);
282
+ };
283
+ exports.convert2String = convert2String;
284
+ const escapeString = (text) => {
285
+ return text.replace(/(?=[<>&"])/g, '\\');
286
+ };
287
+ exports.escapeString = escapeString;
51
288
  //# sourceMappingURL=slotValues.js.map
package/lib/sol2uml.js CHANGED
@@ -13,12 +13,10 @@ const writerFiles_1 = require("./writerFiles");
13
13
  const path_1 = require("path");
14
14
  const squashClasses_1 = require("./squashClasses");
15
15
  const diff_1 = require("./diff");
16
+ const slotValues_1 = require("./slotValues");
17
+ const ethers_1 = require("ethers");
16
18
  const clc = require('cli-color');
17
19
  const program = new commander_1.Command();
18
- const version = (0, path_1.basename)(__dirname) === 'lib'
19
- ? require('../package.json').version // used when run from compile js in /lib
20
- : require('../../package.json').version; // used when run from TypeScript source files under src/ts via ts-node
21
- program.version(version);
22
20
  const debugControl = require('debug');
23
21
  const debug = require('debug')('sol2uml');
24
22
  program
@@ -42,7 +40,15 @@ The Solidity code can be pulled from verified source code on Blockchain explorer
42
40
  .default('mainnet')
43
41
  .env('ETH_NETWORK'))
44
42
  .addOption(new commander_1.Option('-k, --apiKey <key>', 'Blockchain explorer API key. eg Etherscan, Arbiscan, Optimism, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key').env('SCAN_API_KEY'))
43
+ .option('-bc, --backColor <color>', 'Canvas background color. "none" will use a transparent canvas.', 'white')
44
+ .option('-sc, --shapeColor <color>', 'Basic drawing color for graphics, not text', 'black')
45
+ .option('-fc, --fillColor <color>', 'Color used to fill the background of a node', 'gray95')
46
+ .option('-tc, --textColor <color>', 'Color used for text', 'black')
45
47
  .option('-v, --verbose', 'run with debugging statements', false);
48
+ const version = (0, path_1.basename)(__dirname) === 'lib'
49
+ ? require('../package.json').version // used when run from compile js in /lib
50
+ : require('../../package.json').version; // used when run from TypeScript source files under src/ts via ts-node
51
+ program.version(version);
46
52
  program
47
53
  .command('class', { isDefault: true })
48
54
  .description('Generates a UML class diagram from Solidity source code.')
@@ -103,7 +109,7 @@ If an Ethereum address with a 0x prefix is passed, the verified source code from
103
109
  // Convert UML classes to Graphviz dot format.
104
110
  const dotString = (0, converterClasses2Dot_1.convertUmlClasses2Dot)(filteredUmlClasses, combinedOptions.clusterFolders, combinedOptions);
105
111
  // Convert Graphviz dot format to file formats. eg svg or png
106
- await (0, writerFiles_1.writeOutputFiles)(dotString, fileFolderAddress, contractName || 'classDiagram', combinedOptions.outputFormat, combinedOptions.outputFileName);
112
+ await (0, writerFiles_1.writeOutputFiles)(dotString, contractName || 'classDiagram', combinedOptions.outputFormat, combinedOptions.outputFileName);
107
113
  debug(`Finished generating UML`);
108
114
  }
109
115
  catch (err) {
@@ -126,6 +132,7 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
126
132
  .env('NODE_URL')
127
133
  .default('http://localhost:8545'))
128
134
  .option('-bn, --block <number>', 'Block number to get the contract storage values from.', 'latest')
135
+ .option('-a, --array <number>', 'Number of slots to display at the start and end of arrays.', '2')
129
136
  .action(async (fileFolderAddress, options, command) => {
130
137
  try {
131
138
  const combinedOptions = {
@@ -138,12 +145,12 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
138
145
  }
139
146
  let { umlClasses, contractName } = await (0, parserGeneral_1.parserUmlClasses)(fileFolderAddress, combinedOptions);
140
147
  contractName = combinedOptions.contract || contractName;
141
- const storages = (0, converterClasses2Storage_1.convertClasses2Storages)(contractName, umlClasses, combinedOptions.contractFile);
148
+ const arrayItems = parseInt(combinedOptions.array);
149
+ const storageSections = (0, converterClasses2Storage_1.convertClasses2StorageSections)(contractName, umlClasses, arrayItems, combinedOptions.contractFile);
142
150
  if ((0, regEx_1.isAddress)(fileFolderAddress)) {
143
151
  // The first storage is the contract
144
- storages[0].address = fileFolderAddress;
152
+ storageSections[0].address = fileFolderAddress;
145
153
  }
146
- debug(storages);
147
154
  if (combinedOptions.data) {
148
155
  let storageAddress = combinedOptions.storage;
149
156
  if (storageAddress) {
@@ -157,16 +164,24 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k
157
164
  }
158
165
  storageAddress = fileFolderAddress;
159
166
  }
160
- const storage = storages.find((so) => so.name === contractName);
161
- if (!storageAddress)
162
- throw Error(`Could not find the "${contractName}" contract in list of parsed storages`);
163
- await (0, converterClasses2Storage_1.addStorageValues)(combinedOptions.url, storageAddress, storage, combinedOptions.blockNumber);
167
+ let block = combinedOptions.block;
168
+ if (block === 'latest') {
169
+ const provider = new ethers_1.ethers.providers.JsonRpcProvider(combinedOptions.url);
170
+ block = await provider.getBlockNumber();
171
+ debug(`Latest block is ${block}. All storage slot values will be from this block.`);
172
+ }
173
+ // Get slot values for each storage section
174
+ for (const storageSection of storageSections) {
175
+ await (0, slotValues_1.addSlotValues)(combinedOptions.url, storageAddress, storageSection, arrayItems, block);
176
+ // Add storage variables for dynamic arrays, strings and bytes
177
+ await (0, converterClasses2Storage_1.addDynamicVariables)(storageSection, storageSections, combinedOptions.url, storageAddress, arrayItems, block);
178
+ }
164
179
  }
165
- const dotString = (0, converterStorage2Dot_1.convertStorages2Dot)(storages, combinedOptions);
166
- await (0, writerFiles_1.writeOutputFiles)(dotString, fileFolderAddress, contractName || 'storageDiagram', combinedOptions.outputFormat, combinedOptions.outputFileName);
180
+ const dotString = (0, converterStorage2Dot_1.convertStorages2Dot)(storageSections, combinedOptions);
181
+ await (0, writerFiles_1.writeOutputFiles)(dotString, contractName || 'storageDiagram', combinedOptions.outputFormat, combinedOptions.outputFileName);
167
182
  }
168
183
  catch (err) {
169
- console.error(err.stack);
184
+ console.error(err);
170
185
  process.exit(2);
171
186
  }
172
187
  });
@@ -1,8 +1,8 @@
1
1
  import { UmlClass } from './umlClass';
2
2
  /**
3
3
  * Flattens the inheritance hierarchy for each base contract.
4
- * @param umlClasses array of UML classes of type `UMLClass`
4
+ * @param umlClasses array of UML classes of type `UMLClass`. The new squashed class is added to this array.
5
5
  * @param baseContractNames array of contract names to be rendered in squashed format.
6
- * @return squashUmlClasses array of UML classes of type `UMLClass`
6
+ * @return squashUmlClasses array of UML classes of type `UMLClass` that are to be rendered
7
7
  */
8
- export declare const squashUmlClasses: (umlClasses: UmlClass[], baseContractNames: string[]) => UmlClass[];
8
+ export declare const squashUmlClasses: (umlClasses: UmlClass[], baseContractNames: readonly string[]) => UmlClass[];
@@ -29,9 +29,9 @@ const crypto = __importStar(require("crypto"));
29
29
  const debug = require('debug')('sol2uml');
30
30
  /**
31
31
  * Flattens the inheritance hierarchy for each base contract.
32
- * @param umlClasses array of UML classes of type `UMLClass`
32
+ * @param umlClasses array of UML classes of type `UMLClass`. The new squashed class is added to this array.
33
33
  * @param baseContractNames array of contract names to be rendered in squashed format.
34
- * @return squashUmlClasses array of UML classes of type `UMLClass`
34
+ * @return squashUmlClasses array of UML classes of type `UMLClass` that are to be rendered
35
35
  */
36
36
  const squashUmlClasses = (umlClasses, baseContractNames) => {
37
37
  let removedClassIds = [];
@@ -1,7 +1,27 @@
1
1
  export type OutputFormats = 'svg' | 'png' | 'dot' | 'all';
2
- export declare const writeOutputFiles: (dot: string, fileFolderAddress: string, contractName: string, outputFormat?: OutputFormats, outputFilename?: string) => Promise<void>;
2
+ /**
3
+ * Writes output files to the file system based on the provided input and options.
4
+ * @param dot The input string in DOT format.
5
+ * @param contractName The name of the contract.
6
+ * @param outputFormat The format of the output file. choices: svg, png, dot or all. default: png
7
+ * @param outputFilename optional filename of the output file.
8
+ */
9
+ export declare const writeOutputFiles: (dot: string, contractName: string, outputFormat?: OutputFormats, outputFilename?: string) => Promise<void>;
3
10
  export declare function convertDot2Svg(dot: string): any;
4
11
  export declare function writeSolidity(code: string, filename?: string): void;
5
12
  export declare function writeDot(dot: string, filename: string): void;
13
+ /**
14
+ * Writes an SVG file to the file system.
15
+ * @param svg The SVG input to be written to the file system.
16
+ * @param svgFilename The desired file name for the SVG file. default: classDiagram.svg
17
+ * @param outputFormats The format of the output file. choices: svg, png, dot or all. default: png
18
+ * @throws Error - If there is an error writing the SVG file.
19
+ */
6
20
  export declare function writeSVG(svg: any, svgFilename?: string, outputFormats?: OutputFormats): Promise<void>;
21
+ /**
22
+ * Asynchronously writes a PNG file to the file system from an SVG input.
23
+ * @param svg - The SVG input to be converted to a PNG file.
24
+ * @param filename - The desired file name for the PNG file.
25
+ * @throws Error - If there is an error converting or writing the PNG file.
26
+ */
7
27
  export declare function writePng(svg: any, filename: string): Promise<void>;
@@ -9,7 +9,14 @@ const path_1 = __importDefault(require("path"));
9
9
  const sync_1 = __importDefault(require("@aduh95/viz.js/sync"));
10
10
  const { convert } = require('convert-svg-to-png');
11
11
  const debug = require('debug')('sol2uml');
12
- const writeOutputFiles = async (dot, fileFolderAddress, contractName, outputFormat = 'svg', outputFilename) => {
12
+ /**
13
+ * Writes output files to the file system based on the provided input and options.
14
+ * @param dot The input string in DOT format.
15
+ * @param contractName The name of the contract.
16
+ * @param outputFormat The format of the output file. choices: svg, png, dot or all. default: png
17
+ * @param outputFilename optional filename of the output file.
18
+ */
19
+ const writeOutputFiles = async (dot, contractName, outputFormat = 'svg', outputFilename) => {
13
20
  // If all output then extension is svg
14
21
  const outputExt = outputFormat === 'all' ? 'svg' : outputFormat;
15
22
  if (!outputFilename) {
@@ -87,6 +94,13 @@ function writeDot(dot, filename) {
87
94
  });
88
95
  }
89
96
  exports.writeDot = writeDot;
97
+ /**
98
+ * Writes an SVG file to the file system.
99
+ * @param svg The SVG input to be written to the file system.
100
+ * @param svgFilename The desired file name for the SVG file. default: classDiagram.svg
101
+ * @param outputFormats The format of the output file. choices: svg, png, dot or all. default: png
102
+ * @throws Error - If there is an error writing the SVG file.
103
+ */
90
104
  function writeSVG(svg, svgFilename = 'classDiagram.svg', outputFormats = 'png') {
91
105
  debug(`About to write SVN file to ${svgFilename}`);
92
106
  if (outputFormats === 'png') {
@@ -113,6 +127,12 @@ function writeSVG(svg, svgFilename = 'classDiagram.svg', outputFormats = 'png')
113
127
  });
114
128
  }
115
129
  exports.writeSVG = writeSVG;
130
+ /**
131
+ * Asynchronously writes a PNG file to the file system from an SVG input.
132
+ * @param svg - The SVG input to be converted to a PNG file.
133
+ * @param filename - The desired file name for the PNG file.
134
+ * @throws Error - If there is an error converting or writing the PNG file.
135
+ */
116
136
  async function writePng(svg, filename) {
117
137
  // get svg file name from png file name
118
138
  const parsedPngFile = path_1.default.parse(filename);
@@ -142,7 +162,6 @@ async function writePng(svg, filename) {
142
162
  cause: err,
143
163
  });
144
164
  }
145
- console.log(`Generated png file ${pngFilename}`);
146
165
  }
147
166
  exports.writePng = writePng;
148
167
  //# sourceMappingURL=writerFiles.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol2uml",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "description": "Solidity contract visualisation tool.",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -35,11 +35,11 @@
35
35
  "devDependencies": {
36
36
  "@openzeppelin/contracts": "4.8.0",
37
37
  "@types/diff-match-patch": "^1.0.32",
38
- "@types/jest": "^29.2.4",
38
+ "@types/jest": "^29.4.0",
39
39
  "@types/klaw": "^3.0.3",
40
- "jest": "^29.3.1",
41
- "prettier": "^2.8.1",
42
- "ts-jest": "^29.0.3",
40
+ "jest": "^29.4.1",
41
+ "prettier": "^2.8.3",
42
+ "ts-jest": "^29.0.5",
43
43
  "ts-node": "^10.9.1",
44
44
  "typescript": "^4.9.4"
45
45
  },