diff --git a/.changeset/silent-lions-judge.md b/.changeset/silent-lions-judge.md new file mode 100644 index 0000000..33b427f --- /dev/null +++ b/.changeset/silent-lions-judge.md @@ -0,0 +1,5 @@ +--- +"@onflow/flow-cadut": patch +--- + +Fix multiple imports from a single address in one import line diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..72cf948 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +**/templates/*.js +**/generated/**/* +**/dist/**/* +**/*.hbs \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..72cf948 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +**/templates/*.js +**/generated/**/* +**/dist/**/* +**/*.hbs \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index b7ad57f..8487f37 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,164 +1,5 @@ # Flow Cadence Utilities API Reference -## File System - -### sansExtension(filename) - -#### Arguments - -| Name | Type | Description | -| ---------- | ------ | ------------------------ | -| `filename` | string | file name with extension | - -#### Returns - -| Type | Description | -| ------ | -------------------------- | -| string | filename without extension | - -#### Usage - -```javascript -import { sansExtension } from "@onflow/flow-cadut"; - -const fileName = sansExtension("log-message-and-return.cdc"); -console.log({ fileName }); -``` - -📣 This method is used internally to get value for module name during code generation. - -### `readFile(path)` - -Reads the contents fo the file as `utf8`. Syntax sugar for `fs.readFileSync(path, "utf8")` - -#### Arguments - -| Name | Type | Description | -| ------ | ------ | ---------------- | -| `path` | string | path to the file | - -#### Returns - -| Type | Description | -| -------- | -------------------------------------- | -| `string` | string representation of file contents | - -#### Usage - -```javascript -import { clearPath } from "@onflow/flow-cadut"; - -const content = readFile("./log.cdc"); -``` - -### `writeFile(path, data)` - -| Name | Type | Description | -| ------ | ------ | ------------------------------------------ | -| `path` | string | path to file, where data should be written | -| `data` | string | data to write into file | - -📣 If path to the file is nested and does not exist, method will create necessary folders to provide place to accommodate your file. - -#### Usage - -```javascript -import { writeFile } from "@onflow/flow-cadut"; - -const script = ` - pub fun main(){ - log("Hello, Cadence") - } -`; - -writeFile("./cadence/scripts/log.cdc", script); -``` - -### `clearPath(path)` - -Recursively deletes contents of the provided folder and all it's contents. Syntax sugar for `fs.rmdirSync(path, { recursive: true })` - -#### Arguments - -| Name | Type | Description | -| ------ | ------ | ------------------------- | -| `path` | string | path to folder to process | - -#### Usage - -```javascript -import { clearPath } from "@onflow/flow-cadut"; - -clearPath("./ready-to-go"); -``` - -### `getFileList(path)` - -Recursively looking for files under `path` and returns list of paths to found items. - -| Name | Type | Description | -| ------ | ------ | ------------------------- | -| `path` | string | path to folder to process | - -#### Returns - -| Type | Description | -| -------- | ------------------------------------------------------------------------------- | -| [string] | array of strings, representing paths to files contained within specified folder | - -#### Usage - -```javascript -import { getFileList } from "@onflow/flow-cadut"; - -const list = getFileList("./cadence"); -``` - -### `prettify(code, options)` - -Prettifies `code` using Prettier and set of `options`. -Default `options` are: - -```json -{ - "printWidth": 100, - "endOfLine": "lf", - "trailingComma": "es5", - "tabWidth": 2, - "semi": true, - "useTabs": false, - "singleQuote": false -} -``` - -#### Arguments - -| Name | Type | Optional | Description | -| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------- | -| `code` | string | | valid Javascript code | -| `options` | object | ✅ | Prettier options. Consult [Prettier Options Documentation](https://prettier.io/docs/en/options.html) to learn more. | - -#### Returns - -| Type | Description | -| ------ | ----------------------------------- | -| string | prettified version of provided code | - -#### Usage - -```javascript -import { prettify } from "@onflow/flow-cadut"; - -const code = ` - const a = "Hello" - const b = "World - console.log(a +b); -`; - -const pretty = prettify(code); -console.log(pretty); -``` - ## Imports ### `extractImports(code)` diff --git a/docs/generator.md b/docs/generator.md new file mode 100644 index 0000000..104bda2 --- /dev/null +++ b/docs/generator.md @@ -0,0 +1,160 @@ +# Flow Cadut Generator API Reference + +## File System + +### sansExtension(filename) + +#### Arguments + +| Name | Type | Description | +| ---------- | ------ | ------------------------ | +| `filename` | string | file name with extension | + +#### Returns + +| Type | Description | +| ------ | -------------------------- | +| string | filename without extension | + +#### Usage + +```javascript +import { sansExtension } from "@onflow/flow-cadut"; + +const fileName = sansExtension("log-message-and-return.cdc"); +console.log({ fileName }); +``` + +📣 This method is used internally to get value for module name during code generation. + +### `readFile(path)` + +Reads the contents fo the file as `utf8`. Syntax sugar for `fs.readFileSync(path, "utf8")` + +#### Arguments + +| Name | Type | Description | +| ------ | ------ | ---------------- | +| `path` | string | path to the file | + +#### Returns + +| Type | Description | +| -------- | -------------------------------------- | +| `string` | string representation of file contents | + +#### Usage + +```javascript +import { clearPath } from "@onflow/flow-cadut"; + +const content = readFile("./log.cdc"); +``` + +### `writeFile(path, data)` + +| Name | Type | Description | +| ------ | ------ | ------------------------------------------ | +| `path` | string | path to file, where data should be written | +| `data` | string | data to write into file | + +📣 If path to the file is nested and does not exist, method will create necessary folders to provide place to accommodate your file. + +#### Usage + +```javascript +import { writeFile } from "@onflow/flow-cadut"; + +const script = ` + pub fun main(){ + log("Hello, Cadence") + } +`; + +writeFile("./cadence/scripts/log.cdc", script); +``` + +### `clearPath(path)` + +Recursively deletes contents of the provided folder and all it's contents. Syntax sugar for `fs.rmdirSync(path, { recursive: true })` + +#### Arguments + +| Name | Type | Description | +| ------ | ------ | ------------------------- | +| `path` | string | path to folder to process | + +#### Usage + +```javascript +import { clearPath } from "@onflow/flow-cadut"; + +clearPath("./ready-to-go"); +``` + +### `getFileList(path)` + +Recursively looking for files under `path` and returns list of paths to found items. + +| Name | Type | Description | +| ------ | ------ | ------------------------- | +| `path` | string | path to folder to process | + +#### Returns + +| Type | Description | +| -------- | ------------------------------------------------------------------------------- | +| [string] | array of strings, representing paths to files contained within specified folder | + +#### Usage + +```javascript +import { getFileList } from "@onflow/flow-cadut"; + +const list = getFileList("./cadence"); +``` + +### `prettify(code, options)` + +Prettifies `code` using Prettier and set of `options`. +Default `options` are: + +```json +{ + "printWidth": 100, + "endOfLine": "lf", + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "useTabs": false, + "singleQuote": false +} +``` + +#### Arguments + +| Name | Type | Optional | Description | +| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------- | +| `code` | string | | valid Javascript code | +| `options` | object | ✅ | Prettier options. Consult [Prettier Options Documentation](https://prettier.io/docs/en/options.html) to learn more. | + +#### Returns + +| Type | Description | +| ------ | ----------------------------------- | +| string | prettified version of provided code | + +#### Usage + +```javascript +import { prettify } from "@onflow/flow-cadut"; + +const code = ` + const a = "Hello" + const b = "World + console.log(a +b); +`; + +const pretty = prettify(code); +console.log(pretty); +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cc63c39..b33e7fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5866,21 +5866,23 @@ "link": true }, "node_modules/@onflow/flow-js-testing": { - "version": "0.3.0-alpha.11", + "version": "0.3.0-alpha.14", + "resolved": "https://registry.npmjs.org/@onflow/flow-js-testing/-/flow-js-testing-0.3.0-alpha.14.tgz", + "integrity": "sha512-+D33QB0YD1MQj0AUWXuXXLMrSbPfj4ywFh8DWszxpl3njDYCO8QtGZS6Ixq4IG1SnRYOs5c0ZwDa10UVTF9ePQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@onflow/config": "^1.0.3-alpha.0", - "@onflow/fcl": "^1.1.1-alpha.1", + "@onflow/fcl": "^1.2.1-alpha.0", "@onflow/fcl-config": "^0.0.1", - "@onflow/flow-cadut": "^0.2.0-alpha.7", + "@onflow/flow-cadut": "0.2.0-alpha.8", "@onflow/types": "^1.0.3-alpha.0", "elliptic": "^6.5.4", "esm": "^3.2.25", "jest-environment-uint8array": "^1.0.0", + "js-sha256": "^0.9.0", + "js-sha3": "^0.8.0", "rimraf": "^3.0.2", "rlp": "^2.2.6", - "sha3": "^2.1.4", "yargs": "^17.0.1" }, "bin": { @@ -5888,16 +5890,83 @@ } }, "node_modules/@onflow/flow-js-testing/node_modules/@onflow/config": { - "version": "1.0.3-alpha.0", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/config/-/config-1.0.3.tgz", + "integrity": "sha512-ryO0ZXXayz8IKdEdI51PAJgs5WYo7J0Kb+ccNaTS7nRuRq752/r6O8EfqEz3/R2+KsV7XdP3FVhR2tPUhxWhag==", "dev": true, "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-actor": "^1.1.1-alpha.0" + "@onflow/util-actor": "^1.1.1" + } + }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/fcl": { + "version": "1.2.1-alpha.1", + "resolved": "https://registry.npmjs.org/@onflow/fcl/-/fcl-1.2.1-alpha.1.tgz", + "integrity": "sha512-/ms+DK++hxj1TLSRst7X3w3gJc2LbijIgfqFF3LyftDwFmS/pM9Gdyw6REuAHyertRjBDD5JxGEiK8KCQpSfWQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.3", + "@onflow/interaction": "0.0.11", + "@onflow/rlp": "^1.0.2", + "@onflow/sdk": "^1.1.2-alpha.1", + "@onflow/types": "^1.0.3", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "@onflow/util-uid": "^1.0.2" + } + }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/rlp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/rlp/-/rlp-1.0.2.tgz", + "integrity": "sha512-YjIMTQZ7ewYcXsKo6S0dKjUr9uoCFy8NlpH2NX9Xy+L76MQUfJNFJksepDG0HDo8/+9UDdh/cGIbuxW7rUp3QQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6", + "buffer": "^6.0.3" + } + }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/sdk": { + "version": "1.1.2-alpha.1", + "resolved": "https://registry.npmjs.org/@onflow/sdk/-/sdk-1.1.2-alpha.1.tgz", + "integrity": "sha512-tXcKRDuD1nScn/S9hQ4w9glqJiDFRn/BZEz3izlYo0f3ldWXX0J/Jkb/KZ+2IhB2IjP0xwCnT6hB12ov3vjGSw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.3", + "@onflow/rlp": "^1.0.2", + "@onflow/transport-http": "^1.5.0-alpha.1", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "deepmerge": "^4.2.2", + "sha3": "^2.1.4" + } + }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/transport-http": { + "version": "1.5.0-alpha.1", + "resolved": "https://registry.npmjs.org/@onflow/transport-http/-/transport-http-1.5.0-alpha.1.tgz", + "integrity": "sha512-hHZDoWG+/Bp3fAr9i5Lmb3qhH0BEhsB6Y/3xtrg9cm/anNGzmbRKbdKdB/QuCVDMGcjLiOWIq+Z/jh05BxuMVA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "node-fetch": "^2.6.7" } }, "node_modules/@onflow/flow-js-testing/node_modules/@onflow/util-actor": { - "version": "1.1.1-alpha.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@onflow/util-actor/-/util-actor-1.1.1.tgz", + "integrity": "sha512-y74KwQ2T8BUXiP0f+OCifAD1CrBepzCWL1C0lKdSDly7so8RVttc98Hp3oUkDJxoA0KKyAyEjshxw7DSLxYXFw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5905,6 +5974,42 @@ "queue-microtask": "1.1.2" } }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/util-address": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-address/-/util-address-1.0.2.tgz", + "integrity": "sha512-2kjRZK+DxyEoujm4+1gO0lqGFLdaTJC1DuvBF7XqgocmFdayad/OdPFVgaEi06xymmi2kfdn/JFdvBwdZHkJGQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/util-invariant": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-invariant/-/util-invariant-1.0.2.tgz", + "integrity": "sha512-Z5YPAJYUxEoSJ9hGB3jyr0C8gG1VbwX88naF0onBjiMZ89QYbbRG8nup7WWHN2fo/tWo4ElauOpCwU70see0lg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/util-template": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-template/-/util-template-1.0.3.tgz", + "integrity": "sha512-ZBckseo1IwjKO4/F7PvEH4sKRFVAmVAYq0f10Zg79xQ29YF7oU58uXCH4MAjJ8eaZsS5/jeiEif0291bVHH5Rg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/@onflow/flow-js-testing/node_modules/@onflow/util-uid": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-uid/-/util-uid-1.0.2.tgz", + "integrity": "sha512-1BSM0l53QOFmEZ876AX+KdnJmXPRhGlS7vO5WiJULE8GUPyoW6WY2eyk0ZpHjxI0BnKpHOruyZeMilw1jZQSdA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, "node_modules/@onflow/flow-js-testing/node_modules/ansi-styles": { "version": "4.3.0", "dev": true, @@ -6159,6 +6264,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@onflow/types/-/types-1.0.3.tgz", "integrity": "sha512-7za7NgzRvapB50icVmrL21rVHgPaMS/0K9IKXj0FVZRMo3CSI6MV2qLoGftRVX8oDfiH0Lj/1NWD/iSUW6Ed5w==", + "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6" } @@ -6188,6 +6294,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@onflow/util-logger/-/util-logger-1.1.1.tgz", "integrity": "sha512-bVGzjxcLKl4cpb/kFiHtIrdkKDCpZkj1DFMXjhQzpW0MqTmmp1rKf/Fq9B0Y1dbZKh6IxJeGCd5dhNPLmSfb9g==", + "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", "@onflow/config": "^1.0.3" @@ -6197,6 +6304,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@onflow/config/-/config-1.0.3.tgz", "integrity": "sha512-ryO0ZXXayz8IKdEdI51PAJgs5WYo7J0Kb+ccNaTS7nRuRq752/r6O8EfqEz3/R2+KsV7XdP3FVhR2tPUhxWhag==", + "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", "@onflow/util-actor": "^1.1.1" @@ -6206,6 +6314,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@onflow/util-actor/-/util-actor-1.1.1.tgz", "integrity": "sha512-y74KwQ2T8BUXiP0f+OCifAD1CrBepzCWL1C0lKdSDly7so8RVttc98Hp3oUkDJxoA0KKyAyEjshxw7DSLxYXFw==", + "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", "queue-microtask": "1.1.2" @@ -15017,6 +15126,18 @@ "node": ">=8" } }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", + "dev": true + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, "node_modules/js-tokens": { "version": "4.0.0", "dev": true, @@ -20759,7 +20880,7 @@ "devDependencies": { "@babel/core": "^7.15.5", "@babel/preset-env": "^7.15.6", - "@onflow/flow-js-testing": "^0.3.0-alpha.11", + "@onflow/flow-js-testing": "0.3.0-alpha.14", "babel-jest": "^27.2.0", "elliptic": "^6.5.4", "jest": "^27.2.0", @@ -28268,39 +28389,144 @@ } }, "@onflow/flow-js-testing": { - "version": "0.3.0-alpha.11", + "version": "0.3.0-alpha.14", + "resolved": "https://registry.npmjs.org/@onflow/flow-js-testing/-/flow-js-testing-0.3.0-alpha.14.tgz", + "integrity": "sha512-+D33QB0YD1MQj0AUWXuXXLMrSbPfj4ywFh8DWszxpl3njDYCO8QtGZS6Ixq4IG1SnRYOs5c0ZwDa10UVTF9ePQ==", "dev": true, "requires": { - "@onflow/config": "^1.0.3-alpha.0", - "@onflow/fcl": "^1.1.1-alpha.1", + "@onflow/fcl": "^1.2.1-alpha.0", "@onflow/fcl-config": "^0.0.1", - "@onflow/flow-cadut": "^0.2.0-alpha.7", + "@onflow/flow-cadut": "0.2.0-alpha.8", "@onflow/types": "^1.0.3-alpha.0", "elliptic": "^6.5.4", "esm": "^3.2.25", "jest-environment-uint8array": "^1.0.0", + "js-sha256": "^0.9.0", + "js-sha3": "^0.8.0", "rimraf": "^3.0.2", "rlp": "^2.2.6", - "sha3": "^2.1.4", "yargs": "^17.0.1" }, "dependencies": { "@onflow/config": { - "version": "1.0.3-alpha.0", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/config/-/config-1.0.3.tgz", + "integrity": "sha512-ryO0ZXXayz8IKdEdI51PAJgs5WYo7J0Kb+ccNaTS7nRuRq752/r6O8EfqEz3/R2+KsV7XdP3FVhR2tPUhxWhag==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/util-actor": "^1.1.1" + } + }, + "@onflow/fcl": { + "version": "1.2.1-alpha.1", + "resolved": "https://registry.npmjs.org/@onflow/fcl/-/fcl-1.2.1-alpha.1.tgz", + "integrity": "sha512-/ms+DK++hxj1TLSRst7X3w3gJc2LbijIgfqFF3LyftDwFmS/pM9Gdyw6REuAHyertRjBDD5JxGEiK8KCQpSfWQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.3", + "@onflow/interaction": "0.0.11", + "@onflow/rlp": "^1.0.2", + "@onflow/sdk": "^1.1.2-alpha.1", + "@onflow/types": "^1.0.3", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "@onflow/util-uid": "^1.0.2" + } + }, + "@onflow/rlp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/rlp/-/rlp-1.0.2.tgz", + "integrity": "sha512-YjIMTQZ7ewYcXsKo6S0dKjUr9uoCFy8NlpH2NX9Xy+L76MQUfJNFJksepDG0HDo8/+9UDdh/cGIbuxW7rUp3QQ==", "dev": true, "requires": { "@babel/runtime": "^7.18.6", - "@onflow/util-actor": "^1.1.1-alpha.0" + "buffer": "^6.0.3" + } + }, + "@onflow/sdk": { + "version": "1.1.2-alpha.1", + "resolved": "https://registry.npmjs.org/@onflow/sdk/-/sdk-1.1.2-alpha.1.tgz", + "integrity": "sha512-tXcKRDuD1nScn/S9hQ4w9glqJiDFRn/BZEz3izlYo0f3ldWXX0J/Jkb/KZ+2IhB2IjP0xwCnT6hB12ov3vjGSw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.3", + "@onflow/rlp": "^1.0.2", + "@onflow/transport-http": "^1.5.0-alpha.1", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "deepmerge": "^4.2.2", + "sha3": "^2.1.4" + } + }, + "@onflow/transport-http": { + "version": "1.5.0-alpha.1", + "resolved": "https://registry.npmjs.org/@onflow/transport-http/-/transport-http-1.5.0-alpha.1.tgz", + "integrity": "sha512-hHZDoWG+/Bp3fAr9i5Lmb3qhH0BEhsB6Y/3xtrg9cm/anNGzmbRKbdKdB/QuCVDMGcjLiOWIq+Z/jh05BxuMVA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "node-fetch": "^2.6.7" } }, "@onflow/util-actor": { - "version": "1.1.1-alpha.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@onflow/util-actor/-/util-actor-1.1.1.tgz", + "integrity": "sha512-y74KwQ2T8BUXiP0f+OCifAD1CrBepzCWL1C0lKdSDly7so8RVttc98Hp3oUkDJxoA0KKyAyEjshxw7DSLxYXFw==", "dev": true, "requires": { "@babel/runtime": "^7.18.6", "queue-microtask": "1.1.2" } }, + "@onflow/util-address": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-address/-/util-address-1.0.2.tgz", + "integrity": "sha512-2kjRZK+DxyEoujm4+1gO0lqGFLdaTJC1DuvBF7XqgocmFdayad/OdPFVgaEi06xymmi2kfdn/JFdvBwdZHkJGQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "@onflow/util-invariant": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-invariant/-/util-invariant-1.0.2.tgz", + "integrity": "sha512-Z5YPAJYUxEoSJ9hGB3jyr0C8gG1VbwX88naF0onBjiMZ89QYbbRG8nup7WWHN2fo/tWo4ElauOpCwU70see0lg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "@onflow/util-template": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-template/-/util-template-1.0.3.tgz", + "integrity": "sha512-ZBckseo1IwjKO4/F7PvEH4sKRFVAmVAYq0f10Zg79xQ29YF7oU58uXCH4MAjJ8eaZsS5/jeiEif0291bVHH5Rg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "@onflow/util-uid": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-uid/-/util-uid-1.0.2.tgz", + "integrity": "sha512-1BSM0l53QOFmEZ876AX+KdnJmXPRhGlS7vO5WiJULE8GUPyoW6WY2eyk0ZpHjxI0BnKpHOruyZeMilw1jZQSdA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.18.6" + } + }, "ansi-styles": { "version": "4.3.0", "dev": true, @@ -30326,7 +30552,7 @@ "@babel/core": "^7.15.5", "@babel/preset-env": "^7.15.6", "@onflow/fcl": "^1.1.1-alpha.1", - "@onflow/flow-js-testing": "^0.3.0-alpha.11", + "@onflow/flow-js-testing": "0.3.0-alpha.14", "babel-jest": "^27.2.0", "elliptic": "^6.5.4", "jest": "^27.2.0", @@ -35210,6 +35436,18 @@ } } }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", + "dev": true + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "dev": true diff --git a/package.json b/package.json index 6d77557..0d9ec84 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,6 @@ "prettier": "^2.7.1" }, "eslintConfig": { - "ignorePatterns": [ - "**/templates/*.js", - "**/generated/**/*", - "**/dist/**/*", - "**/*.hbs" - ], "extends": [ "eslint:recommended", "plugin:jest/recommended", diff --git a/packages/dev-test/package.json b/packages/dev-test/package.json index 8f17b15..8bd65a4 100644 --- a/packages/dev-test/package.json +++ b/packages/dev-test/package.json @@ -13,7 +13,7 @@ "devDependencies": { "@babel/core": "^7.15.5", "@babel/preset-env": "^7.15.6", - "@onflow/flow-js-testing": "^0.3.0-alpha.11", + "@onflow/flow-js-testing": "0.3.0-alpha.14", "babel-jest": "^27.2.0", "elliptic": "^6.5.4", "jest": "^27.2.0", diff --git a/packages/dev-test/test/interaction.test.js b/packages/dev-test/test/interaction.test.js index 9f90ddd..7caca9f 100644 --- a/packages/dev-test/test/interaction.test.js +++ b/packages/dev-test/test/interaction.test.js @@ -82,13 +82,9 @@ describe("arguments - scripts", () => { describe("multiple interactions", () => { beforeAll(async () => { const basePath = path.resolve(__dirname, "../cadence") - // You can specify different port to parallelize execution of describe blocks - const port = 8080 - // Setting logging flag to true will pipe emulator output to console - const logging = true - await init(basePath, {port, logging}) - return emulator.start(port) + await init(basePath) + return emulator.start() }) // Stop emulator, so it could be restarted diff --git a/packages/flow-cadut-generator/src/test/file.test.js b/packages/flow-cadut-generator/src/test/file.test.js new file mode 100644 index 0000000..1b6653b --- /dev/null +++ b/packages/flow-cadut-generator/src/test/file.test.js @@ -0,0 +1,9 @@ +const {sansExtension} = require("../file") + +describe("file tests", () => { + // Files + it("should strip extension from filename", function () { + const fileName = sansExtension("log-message-and-return.cdc") + expect(fileName).toBe("log-message-and-return") + }) +}) diff --git a/packages/flow-cadut-views/src/test/try-views.test.js b/packages/flow-cadut-views/src/test/try-views.test.js index d6b4a2b..c0d9097 100644 --- a/packages/flow-cadut-views/src/test/try-views.test.js +++ b/packages/flow-cadut-views/src/test/try-views.test.js @@ -28,6 +28,7 @@ describe("views", () => { it("fetch motogp", async () => { await setEnvironment("mainnet") const [cards, err] = await getView(MotoGP, "0x53f389d96fb4ce5e") + console.log(err) expect(cards).toBeTruthy() expect(err).toBeNull() }) diff --git a/packages/flow-cadut/src/imports.js b/packages/flow-cadut/src/imports.js index 01d83e8..849d8d4 100644 --- a/packages/flow-cadut/src/imports.js +++ b/packages/flow-cadut/src/imports.js @@ -16,18 +16,37 @@ * limitations under the License. */ -const getPairs = line => { - return line - .split(/\s/) - .map(item => item.replace(/\s/g, "")) - .filter(item => item.length > 0 && item !== "import" && item !== "from") -} +/* + === REGEXP_IMPORT explanation === + Matches import line in cadence code and is used for extracting address & list of contracts imported -const collect = (acc, item) => { - const [contract, address] = item - acc[contract] = address - return acc -} + / => start of regexp + import\s+ => should have keyword import followed by one or more spaces + + ((([\w\d]+)(\s*,\s*))*[\w\d]+) => >>MATCH[1]<< matcher group for imported contracts (one or more comma separated words including digits) + + ([\w\d]+\s*,\s*)* => match comma-separated contracts + [\w\d]+ => match individual contract name (one or more word or digit) + \s*,\s* => match trailing comma with any amount of space separation + + [\w\d]+ => match last contract name (mustn't have trailing comma, so separate from previous matcher) + + \s+from\s+ => keyword from with one or more leading and following space characters + ([\w\d".\\/]+) => >>MATCH[3]<< one or more word, digit, "" or / character for address or file import notation + / => end of regexp +*/ +export const REGEXP_IMPORT = + /import\s+(([\w\d]+\s*,\s*)*[\w\d]+)\s+from\s*([\w\d".\\/]+)/g + +/* + === REGEXP_IMPORT_CONTRACT === + Used to separate individual contract names from comma/space separarated list of contracts + + / => start of regexp + ([\w\d]+) => >>MATCH[1]<< match individual contract name (one or more word or digit) + /g => end of regexp, g - global flag (find all) +*/ +export const REGEXP_IMPORT_CONTRACT = /([\w\d]+)/g /** * Returns address map for contracts defined in template code. @@ -38,10 +57,16 @@ export const extractImports = code => { if (!code || code.length === 0) { return {} } - const split = code.split("\n") - const filtered = split.filter(line => /^\s*import\s+\w*\s+from/.test(line)) - const mapped = filtered.map(getPairs) - return mapped.reduce(collect, {}) + + return [...code.matchAll(REGEXP_IMPORT)].reduce((contracts, match) => { + const contractsStr = match[1], + address = match[3] + + contractsStr.match(REGEXP_IMPORT_CONTRACT).forEach(contract => { + contracts[contract] = address + }) + return contracts + }, {}) } /** @@ -89,8 +114,6 @@ export const reportMissingImports = (code, addressMap, prefix = "") => { } } -const REGEXP_IMPORT = /(\s*import\s*)([\w\d]+)(\s+from\s*)([\w\d".\\/]+)/g - /** * Returns Cadence template code with replaced import addresses * @param {string} code - Cadence template code. @@ -100,13 +123,25 @@ const REGEXP_IMPORT = /(\s*import\s*)([\w\d]+)(\s+from\s*)([\w\d".\\/]+)/g * @returns {*} */ export const replaceImportAddresses = (code, addressMap, byName = true) => { - return code.replace(REGEXP_IMPORT, (match, imp, contract, _, address) => { - const key = byName ? contract : address - const newAddress = - addressMap instanceof Function ? addressMap(key) : addressMap[key] - - // If the address is not inside addressMap we shall not alter import statement - const validAddress = newAddress || address - return `${imp}${contract} from ${validAddress}` + return code.replace(REGEXP_IMPORT, importLine => { + const contracts = extractImports(importLine) + const contractMap = Object.keys(contracts).reduce((map, contract) => { + const address = contracts[contract] + const key = byName ? contract : address + const newAddress = + addressMap instanceof Function ? addressMap(key) : addressMap[key] + + // If the address is not inside addressMap we shall not alter import statement + const validAddress = newAddress || address + map[validAddress] = (map[validAddress] ?? []).concat(contract) + return map + }, {}) + + return Object.keys(contractMap) + .reduce((res, addr) => { + const contractsStr = contractMap[addr].join(", ") + return res.concat(`import ${contractsStr} from ${addr}`) + }, []) + .join("\n") }) } diff --git a/packages/flow-cadut-generator/src/test/example.test.js b/packages/flow-cadut/tests/example.test.js similarity index 93% rename from packages/flow-cadut-generator/src/test/example.test.js rename to packages/flow-cadut/tests/example.test.js index 0f7bbcc..036376e 100644 --- a/packages/flow-cadut-generator/src/test/example.test.js +++ b/packages/flow-cadut/tests/example.test.js @@ -1,16 +1,21 @@ -// files -import {sansExtension} from "../" - // imports import { extractImports, missingImports, report, replaceImportAddresses, -} from "@onflow/flow-cadut" +} from "../src/imports" // arguments -import {mapArgument, mapArguments, mapValuesToCode} from "@onflow/flow-cadut" +import { + mapArgument, + mapArguments, + mapValuesToCode, + splitArgs, + argType, + getDictionaryTypes, + getArrayType, +} from "../src/args" // parser import { @@ -22,25 +27,12 @@ import { extractScriptArguments, extractTransactionArguments, extractContractName, - splitArgs, - argType, - getDictionaryTypes, - getArrayType, -} from "@onflow/flow-cadut" +} from "../src/parser" // Interactions -import {setEnvironment, getEnvironment} from "@onflow/flow-cadut" - -// Templates -import "../templates" +import {setEnvironment, getEnvironment} from "../src/env" describe("documentation examples", function () { - // Files - it("should strip extension from filename", function () { - const fileName = sansExtension("log-message-and-return.cdc") - expect(fileName).toBe("log-message-and-return") - }) - // Imports it("should return import list", function () { const code = ` @@ -96,6 +88,21 @@ describe("documentation examples", function () { ) }) + it("should replace import for multiple imports", function () { + const code = ` + import Messages, GiraffeNFT from 0x01 + pub fun main(){} + ` + const addressMap = { + Messages: "0xf8d6e0586b0a20c7", + GiraffeNFT: "0xf8d6e0586b0a20c7", + } + const replaced = replaceImportAddresses(code, addressMap) + expect( + replaced.includes("import Messages, GiraffeNFT from 0xf8d6e0586b0a20c7") + ).toBe(true) + }) + // Arguments it("should convert value to sdk argument", async function () { const type = "String" diff --git a/packages/flow-cadut/tests/imports.test.js b/packages/flow-cadut/tests/imports.test.js index 8061773..8261da8 100644 --- a/packages/flow-cadut/tests/imports.test.js +++ b/packages/flow-cadut/tests/imports.test.js @@ -1,7 +1,81 @@ -import {extractImports} from "../src" +import {extractImports, replaceImportAddresses} from "../src" +import {REGEXP_IMPORT, REGEXP_IMPORT_CONTRACT} from "../src/imports" + +describe("imports RegExp tests", () => { + it("REGEXP_IMPORT - shall match import with one contract", () => { + const test = "import Foo from 0x01" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match[1]).toBe("Foo") + expect(match[3]).toBe("0x01") + }) + + it("REGEXP_IMPORT - shall match import with integer in name", () => { + const test = "import F2oo from 0x01" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match[1]).toBe("F2oo") + expect(match[3]).toBe("0x01") + }) + + it("REGEXP_IMPORT - shall match import with one filename", () => { + const test = 'import Foo from "Foo.cdc"' + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match[1]).toBe("Foo") + expect(match[3]).toBe('"Foo.cdc"') + }) + + it("REGEXP_IMPORT - shall match import with multiple contracts", () => { + const test = "import Foo, Bar from 0x01" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match[1]).toEqual("Foo, Bar") + expect(match[3]).toEqual("0x01") + }) + + it("REGEXP_IMPORT - shall match import with multiple contracts with variable whitespace", () => { + const test = "import Foo, Bar from 0x01" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match[1]).toEqual("Foo, Bar") + expect(match[3]).toEqual("0x01") + }) + + it("REGEXP_IMPORT - shall not match import with trailing comma", () => { + const test = "import Foo, from 0x01" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match).toEqual(undefined) + }) + + it("REGEXP_IMPORT - shall not match without import tag", () => { + const test = "impgsaorst Foo from 0x01" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match).toEqual(undefined) + }) + + it("REGEXP_IMPORT - shall not match without import address", () => { + const test = "import Foo from" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match).toEqual(undefined) + }) + + it("REGEXP_IMPORT - shall not match without space preceeding imports", () => { + const test = "importFoo, Bar from 0x123" + const [match] = test.matchAll(REGEXP_IMPORT) + expect(match).toEqual(undefined) + }) + + it("REGEXP_IMPORT_CONTRACT - shall extract comma separated contract names", () => { + const test = "Foo, Bar" + const match = test.match(REGEXP_IMPORT_CONTRACT) + expect(match).toEqual(["Foo", "Bar"]) + }) + + it("REGEXP_IMPORT_CONTRACT - shall extract contract names with integers", () => { + const test = "Fo2o, Ba5r" + const match = test.match(REGEXP_IMPORT_CONTRACT) + expect(match).toEqual(["Fo2o", "Ba5r"]) + }) +}) describe("imports tests", () => { - it("shall return a list of imports", () => { + it("extractImports - shall return a list of imports", () => { const code = ` import HelloWorld from 0x01 ` @@ -9,7 +83,7 @@ describe("imports tests", () => { expect(imports["HelloWorld"]).toBe("0x01") }) - it("shall return a list for multiple imports", () => { + it("extractImports - shall return a list for multiple imports", () => { const code = ` import First from 0x01 import Second from 0x02 @@ -19,7 +93,7 @@ describe("imports tests", () => { expect(imports["Second"]).toBe("0x02") }) - it("shall skip import keyword in comments", () => { + it("extractImports - shall skip import keyword in comments", () => { const code = ` // this code will mention import in comments block // and then try to import from 0x01 @@ -28,4 +102,56 @@ describe("imports tests", () => { const imports = extractImports(code) expect(imports["HelloWorld"]).toBe("0x01") }) + + it("extractImports - shall work with multiple imports", () => { + const code = ` + import HelloWorld, GiraffeNFT from 0x01 + ` + const imports = extractImports(code) + expect(imports["HelloWorld"]).toBe("0x01") + }) + + it("replaceImportAddresses - should replace single import addresses", function () { + const code = ` + import Messages from 0x01 + pub fun main(){} + ` + const addressMap = { + Messages: "0xf8d6e0586b0a20c7", + } + const replaced = replaceImportAddresses(code, addressMap) + expect(replaced.includes("import Messages from 0xf8d6e0586b0a20c7")).toBe( + true + ) + }) + + it("replaceImportAddresses - should replace import for multiple imports", function () { + const code = ` + import Messages, GiraffeNFT from 0x01 + pub fun main(){} + ` + const addressMap = { + Messages: "0xf8d6e0586b0a20c7", + GiraffeNFT: "0xf8d6e0586b0a20c7", + } + const replaced = replaceImportAddresses(code, addressMap) + expect( + replaced.includes("import Messages, GiraffeNFT from 0xf8d6e0586b0a20c7") + ).toBe(true) + }) + + it("replaceImportAddresses - should replace import for multiple imports with whitespace", function () { + const code = ` + import Messages, GiraffeNFT from 0x01 + pub fun main(){} + ` + const addressMap = { + Messages: "0xf8d6e0586b0a20c7", + GiraffeNFT: "0xf8d6e0586b0a20c7", + } + const replaced = replaceImportAddresses(code, addressMap) + expect( + replaced.includes("import Messages, GiraffeNFT from 0xf8d6e0586b0a20c7") + ).toBe(true) + }) })