From b031539a38c3ec34a796272bf594e2428b5728de Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sat, 27 Apr 2024 12:47:58 +0200 Subject: [PATCH 01/37] chore: add meow, clean up the tact entry point --- bin/tact | 57 ++++++++++++++++++++++++---------------------------- package.json | 1 + yarn.lock | 5 +++++ 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/bin/tact b/bin/tact index 1f6016c27..60a53562e 100755 --- a/bin/tact +++ b/bin/tact @@ -1,38 +1,33 @@ #!/usr/bin/env node + const main = require("../dist/node"); -const arg = require("arg"); +const meowModule = import("meow"); -// Resolve arguments -const args = arg({ - "--config": String, - "--project": String, - "--version": Boolean, -}); +meowModule.then((meow) => { + const importMeta = { url: new URL("file://" + __dirname + __filename) }; + const cli = meow.default(` + Usage + $ tact -if (args["--version"]) { - console.log("1.2.0"); - return; -} + Options + --project , -r + --version Prints the current tact version + --help Displays this text -if (!args["--config"]) { - console.log("USAGE: tact --config [--project { - try { - const success = await main.run({ - configPath: args["--config"], - projectNames: args["--project"] ? args["--project"] : [], - }); - // https://nodejs.org/docs/v20.12.1/api/process.html#exit-codes - if (!success) process.exit(30); - } catch (e) { - console.warn( - "Internal compiler error. Please, report it to https://github.com/tact-lang/tact/issues.", - ); - console.log(e); - process.exit(30); + if (cli.flags.version) { + cli.showVersion(); + return; } -})(); + + if (!cli.input.at(0)) { + cli.showHelp(); + return; + } + + return main.run({ configPath: cli.input.at(0), projectNames: cli.flags.project ?? [] }) +}); diff --git a/package.json b/package.json index 1fc2ef9b9..f99d1e93c 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "blockstore-core": "1.0.5", "change-case": "^4.1.2", "ipfs-unixfs-importer": "9.0.10", + "meow": "^13.2.0", "mkdirp": "^2.1.3", "multiformats": "^13.1.0", "ohm-js": "^17.1.0", diff --git a/yarn.lock b/yarn.lock index 9d4acc0bd..4f65325ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4105,6 +4105,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +meow@^13.2.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-13.2.0.tgz#6b7d63f913f984063b3cc261b6e8800c4cd3474f" + integrity sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA== + merge-options@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-3.0.4.tgz#84709c2aa2a4b24c1981f66c179fe5565cc6dbb7" From 46bea4006bba2acf79f7f84c0b72f5350706b185 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sat, 27 Apr 2024 12:49:48 +0200 Subject: [PATCH 02/37] chore: remove tabs from tact script --- bin/tact | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/tact b/bin/tact index 60a53562e..99cf1570e 100755 --- a/bin/tact +++ b/bin/tact @@ -7,16 +7,16 @@ meowModule.then((meow) => { const importMeta = { url: new URL("file://" + __dirname + __filename) }; const cli = meow.default(` Usage - $ tact + $ tact Options - --project , -r - --version Prints the current tact version + --project , -r + --version Prints the current tact version --help Displays this text Examples - $ tact --version - 1.2.0 + $ tact --version + 1.2.0 `, { importMeta: importMeta, flags: { project: { shortFlag: "p", type: "string", isMultiple: true }, version: { shortFlag: "v", type: "boolean" }} }); if (cli.flags.version) { From 3c397745cfea54fa4aae146ed9fa952d04a216d9 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sat, 27 Apr 2024 20:08:45 +0200 Subject: [PATCH 03/37] feat: add --func and --check --- bin/tact | 31 +++++++++++++++++++++++++++---- package.json | 4 ++-- src/node.ts | 6 +++++- src/pipeline/build.ts | 16 ++++++++++++++++ yarn.lock | 2 +- 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/bin/tact b/bin/tact index 99cf1570e..45fb332be 100755 --- a/bin/tact +++ b/bin/tact @@ -5,19 +5,32 @@ const meowModule = import("meow"); meowModule.then((meow) => { const importMeta = { url: new URL("file://" + __dirname + __filename) }; - const cli = meow.default(` + const cli = meow.default( + ` Usage $ tact Options --project , -r --version Prints the current tact version + --func Outputs the intermediate FunC code. + --check Performs type checking and stops. --help Displays this text Examples $ tact --version 1.2.0 - `, { importMeta: importMeta, flags: { project: { shortFlag: "p", type: "string", isMultiple: true }, version: { shortFlag: "v", type: "boolean" }} }); + `, + { + importMeta: importMeta, + flags: { + project: { shortFlag: "p", type: "string", isMultiple: true }, + version: { shortflag: "v", type: "boolean" }, + check: { shortflag: "c", type: "boolean" }, + func: { shortFlag: "f", type: "boolean" }, + }, + }, + ); if (cli.flags.version) { cli.showVersion(); @@ -28,6 +41,16 @@ meowModule.then((meow) => { cli.showHelp(); return; } - - return main.run({ configPath: cli.input.at(0), projectNames: cli.flags.project ?? [] }) + + if (cli.flags.check && cli.flags.func) { + console.log("--func and --check are mutually exclusive"); + process.exit(30); + } + + return main.run({ + configPath: cli.input.at(0), + projectNames: cli.flags.project ?? [], + checkOnly: cli.flags.check, + func: cli.flags.func, + }); }); diff --git a/package.json b/package.json index f99d1e93c..ec4a91607 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "@tact-lang/opcode": "^0.0.14", "@ton/core": "0.56.3", "@ton/crypto": "^3.2.0", - "arg": "^5.0.2", "blockstore-core": "1.0.5", "change-case": "^4.1.2", "ipfs-unixfs-importer": "9.0.10", @@ -92,5 +91,6 @@ "filename": "CHANGELOG.md" } } - } + }, + "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" } diff --git a/src/node.ts b/src/node.ts index cd7b1ebcb..a22edbfcd 100644 --- a/src/node.ts +++ b/src/node.ts @@ -8,6 +8,8 @@ import { consoleLogger } from "./logger"; export async function run(args: { configPath: string; projectNames?: string[]; + checkOnly?: boolean; + func?: boolean; }) { // Load config const resolvedPath = path.resolve(args.configPath); @@ -28,7 +30,7 @@ export async function run(args: { // Resolve projects let projects = config.projects; if (args.projectNames && args.projectNames.length > 0) { - // Check that all proejct names are valid + // Check that all project names are valid for (const pp of args.projectNames) { if (!projects.find((v) => v.name === pp)) { console.warn("Unable to find project " + pp); @@ -58,6 +60,8 @@ export async function run(args: { project, stdlib, logger: consoleLogger, + checkOnly: args.checkOnly, + func: args.func, }); success = success && built; } diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index a10cdf012..f6a842980 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -26,6 +26,8 @@ export async function build(args: { project: VirtualFileSystem; stdlib: string | VirtualFileSystem; logger?: TactLogger | null | undefined; + checkOnly?: boolean; + func?: boolean; }) { const { config, project } = args; const stdlib = @@ -68,6 +70,11 @@ export async function build(args: { return false; } + if (args.checkOnly) { + logger.log("✔️ Type checking succeeded."); + return true; + } + // Compile contracts let ok = true; const built: { @@ -127,6 +134,10 @@ export async function build(args: { continue; } + if (args.func) { + continue; + } + // Compiling contract to TVM logger.log(" > " + contract + ": func compiler"); let codeBoc: Buffer; @@ -198,6 +209,11 @@ export async function build(args: { return false; } + if (args.func) { + logger.log("✔️ FunC code generation succeeded."); + return true; + } + // Package logger.log(" > Packaging"); const contracts = getContracts(ctx); diff --git a/yarn.lock b/yarn.lock index 4f65325ef..107bf85c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1313,7 +1313,7 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -arg@^5.0.1, arg@^5.0.2: +arg@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== From 650d3cb8e91bdf513be181acac846e468370a3b5 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sat, 27 Apr 2024 20:10:11 +0200 Subject: [PATCH 04/37] fix: remove extra packageManager entry from package.json --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index ec4a91607..71b0649dd 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,5 @@ "filename": "CHANGELOG.md" } } - }, - "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" + } } From ce957f6198908a7f350b09fbcee59d509fe8561b Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 07:59:16 +0200 Subject: [PATCH 05/37] fix: reintroduce --config and move bin/tact to bin/tact.mjs --- bin/tact | 56 --------------------------------------------------- bin/tact.mjs | 50 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +++-- tsconfig.json | 2 +- 4 files changed, 54 insertions(+), 59 deletions(-) delete mode 100755 bin/tact create mode 100755 bin/tact.mjs diff --git a/bin/tact b/bin/tact deleted file mode 100755 index 45fb332be..000000000 --- a/bin/tact +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env node - -const main = require("../dist/node"); -const meowModule = import("meow"); - -meowModule.then((meow) => { - const importMeta = { url: new URL("file://" + __dirname + __filename) }; - const cli = meow.default( - ` - Usage - $ tact - - Options - --project , -r - --version Prints the current tact version - --func Outputs the intermediate FunC code. - --check Performs type checking and stops. - --help Displays this text - - Examples - $ tact --version - 1.2.0 - `, - { - importMeta: importMeta, - flags: { - project: { shortFlag: "p", type: "string", isMultiple: true }, - version: { shortflag: "v", type: "boolean" }, - check: { shortflag: "c", type: "boolean" }, - func: { shortFlag: "f", type: "boolean" }, - }, - }, - ); - - if (cli.flags.version) { - cli.showVersion(); - return; - } - - if (!cli.input.at(0)) { - cli.showHelp(); - return; - } - - if (cli.flags.check && cli.flags.func) { - console.log("--func and --check are mutually exclusive"); - process.exit(30); - } - - return main.run({ - configPath: cli.input.at(0), - projectNames: cli.flags.project ?? [], - checkOnly: cli.flags.check, - func: cli.flags.func, - }); -}); diff --git a/bin/tact.mjs b/bin/tact.mjs new file mode 100755 index 000000000..1bed6b2a2 --- /dev/null +++ b/bin/tact.mjs @@ -0,0 +1,50 @@ +#!/usr/bin/env node + +import * as main from "../dist/node.js"; +import meow from "meow"; + +const cli = meow( + ` +Usage + $ tact --config + +Options + --project , -r + --version Prints the current tact version + --func Outputs the intermediate FunC code. + --check Performs type checking and stops. + --help Displays this text + +Examples + $ tact --version + 1.2.0 +`, + { + importMeta: import.meta, + flags: { + config: { shortFlags: "c", type: "string", isRequired: true }, + project: { shortFlag: "p", type: "string", isMultiple: true }, + version: { shortflag: "v", type: "boolean" }, + check: { shortflag: "c", type: "boolean" }, + func: { shortFlag: "f", type: "boolean" }, + }, + }, +); + +if (cli.flags.version) { + cli.showVersion(); +} + +if (cli.flags.check && cli.flags.func) { + console.log("--func and --check are mutually exclusive"); + process.exit(30); +} + +const success = await main.run({ + configPath: cli.flags.config, + projectNames: cli.flags.project ?? [], + checkOnly: cli.flags.check, + func: cli.flags.func, +}); + +process.exit(success ? 0 : 30); diff --git a/package.json b/package.json index 71b0649dd..8071bc056 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ ], "main": "./dist/main.js", "bin": { - "tact": "./bin/tact" + "tact": "./bin/tact.mjs" }, "dependencies": { "@ipld/dag-pb": "2.1.18", @@ -91,5 +91,6 @@ "filename": "CHANGELOG.md" } } - } + }, + "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" } diff --git a/tsconfig.json b/tsconfig.json index 757a55252..976a8f221 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -63,6 +63,6 @@ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "resolveJsonModule": true }, - "include": ["src/**/*"], + "include": ["src/**/*", "bin/tact.mjs"], "exclude": ["**/**.spec.ts", "**/**.bind.ts", "src/test/features/output/**/*"] } From ce2a40cec29b69cb8119655cee383b5ad97138a9 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 08:01:27 +0200 Subject: [PATCH 06/37] fix: remove extra packageManager entry from package.json and tsconfig.json changes --- package.json | 3 +-- tsconfig.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8071bc056..9c6bd6f2a 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,5 @@ "filename": "CHANGELOG.md" } } - }, - "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" + } } diff --git a/tsconfig.json b/tsconfig.json index 976a8f221..757a55252 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -63,6 +63,6 @@ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "resolveJsonModule": true }, - "include": ["src/**/*", "bin/tact.mjs"], + "include": ["src/**/*"], "exclude": ["**/**.spec.ts", "**/**.bind.ts", "src/test/features/output/**/*"] } From f13ce1e52967bf86e943eecfeb59350f46171059 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 08:07:42 +0200 Subject: [PATCH 07/37] fix: rename references to bin/tact in the GitHub workflow --- .github/workflows/tact.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 51cbdebbd..c60c6331e 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -52,14 +52,14 @@ jobs: - name: Compare Tact version from CLI flag `--version` against package.json if: runner.os != 'Windows' run: | - if [ "$(./bin/tact --version)" != "$(jq -r '.version' < package.json)" ]; + if [ "$(./bin/tact.mjs --version)" != "$(jq -r '.version' < package.json)" ]; then false fi - name: Tact CLI non-zero exit code if: runner.os != 'Windows' run: | - ! ./bin/tact --config test/tact-cli/tact.config.json + ! ./bin/tact.mjs --config test/tact-cli/tact.config.json - name: Link Tact compiler run: | From 08ba95e1e583b5f23af0d11fe7a80dbccb7e95ab Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 08:12:21 +0200 Subject: [PATCH 08/37] chore: update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de51ad24b..51f329f87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `isEmpty` extension function for the `Map` type: PR [#266](https://github.com/tact-lang/tact/pull/266) - The `pow2` power function with base 2: PR [#267](https://github.com/tact-lang/tact/pull/267) - The `try` and `try-catch` statements: PR [#212](https://github.com/tact-lang/tact/pull/212) +- The `--func` and `--check` flags: PR [#287](https://github.com/tact-lang/tact/pull/287) ### Changed From 5b76f06fd441cad9edfaa390d6877d22f6ca494b Mon Sep 17 00:00:00 2001 From: Vitor Py <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 10:40:29 +0200 Subject: [PATCH 09/37] Update bin/tact.mjs Co-authored-by: Anton Trunov --- bin/tact.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tact.mjs b/bin/tact.mjs index 1bed6b2a2..c4211a929 100755 --- a/bin/tact.mjs +++ b/bin/tact.mjs @@ -9,7 +9,7 @@ Usage $ tact --config Options - --project , -r + --project , -p --version Prints the current tact version --func Outputs the intermediate FunC code. --check Performs type checking and stops. From df99e2af6de4cd5912e65a212b70fd19df5973ec Mon Sep 17 00:00:00 2001 From: Vitor Py <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 10:40:36 +0200 Subject: [PATCH 10/37] Update bin/tact.mjs Co-authored-by: Anton Trunov --- bin/tact.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tact.mjs b/bin/tact.mjs index c4211a929..007652317 100755 --- a/bin/tact.mjs +++ b/bin/tact.mjs @@ -10,7 +10,7 @@ Usage Options --project , -p - --version Prints the current tact version + --version Prints the current Tact version --func Outputs the intermediate FunC code. --check Performs type checking and stops. --help Displays this text From b061500ba6fb325da203576a5612ae66e4c94f91 Mon Sep 17 00:00:00 2001 From: Vitor Py <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 10:40:45 +0200 Subject: [PATCH 11/37] Update bin/tact.mjs Co-authored-by: Anton Trunov --- bin/tact.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tact.mjs b/bin/tact.mjs index 007652317..3c9671087 100755 --- a/bin/tact.mjs +++ b/bin/tact.mjs @@ -12,7 +12,7 @@ Options --project , -p --version Prints the current Tact version --func Outputs the intermediate FunC code. - --check Performs type checking and stops. + --check Performs type checking and stops --help Displays this text Examples From efe05018b3e168725962df8cfabd7f7d51a548cd Mon Sep 17 00:00:00 2001 From: Vitor Py <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 11:28:43 +0200 Subject: [PATCH 12/37] Update bin/tact.mjs Co-authored-by: Anton Trunov --- bin/tact.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tact.mjs b/bin/tact.mjs index 3c9671087..b3bc44c0f 100755 --- a/bin/tact.mjs +++ b/bin/tact.mjs @@ -11,7 +11,7 @@ Usage Options --project , -p --version Prints the current Tact version - --func Outputs the intermediate FunC code. + --func Outputs the intermediate FunC code and stops --check Performs type checking and stops --help Displays this text From cb0455bb47c8f5240c1dc25f5395dc6a9cf5a95d Mon Sep 17 00:00:00 2001 From: Vitor Py <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 11:29:30 +0200 Subject: [PATCH 13/37] Update CHANGELOG.md Co-authored-by: Anton Trunov --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05fa833ae..756d25051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `isEmpty` extension function for the `Map` type: PR [#266](https://github.com/tact-lang/tact/pull/266) - The `pow2` power function with base 2: PR [#267](https://github.com/tact-lang/tact/pull/267) - The `try` and `try-catch` statements: PR [#212](https://github.com/tact-lang/tact/pull/212) -- The `--func` and `--check` flags: PR [#287](https://github.com/tact-lang/tact/pull/287) +- The `--func` and `--check` command-line flags: PR [#287](https://github.com/tact-lang/tact/pull/287) - The `del` method for the `Map` type: PR [#95](https://github.com/tact-lang/tact/pull/95) ### Changed From d757a47192216ab701e3f890fa8cfa212d695ae5 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Sun, 28 Apr 2024 19:36:00 +0200 Subject: [PATCH 14/37] feat: compile single file --- .github/workflows/tact.yml | 9 ++++++-- bin/tact.mjs | 28 ++++++++++++++++++----- package.json | 3 ++- src/config/parseConfig.ts | 3 +++ src/node.ts | 47 ++++++++++++++++++++++++++++++-------- 5 files changed, 71 insertions(+), 19 deletions(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index d7ed18d01..70c5819b4 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -53,18 +53,23 @@ jobs: if: runner.os == 'Windows' run: | type examples\output\echo_Echo.pkg + + - name: Install the package locally + if: runner.os == 'Windows' + run: | + npm install -g ./ - name: Compare Tact version from CLI flag `--version` against package.json if: runner.os != 'Windows' run: | - if [ "$(./bin/tact.mjs --version)" != "$(jq -r '.version' < package.json)" ]; + if [ "$(tact --version)" != "$(jq -r '.version' < package.json)" ]; then false fi - name: Tact CLI non-zero exit code if: runner.os != 'Windows' run: | - ! ./bin/tact.mjs --config test/tact-cli/tact.config.json + ! tact --config test/tact-cli/tact.config.json - name: Link Tact compiler run: | diff --git a/bin/tact.mjs b/bin/tact.mjs index b3bc44c0f..988eacd3d 100755 --- a/bin/tact.mjs +++ b/bin/tact.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node +import pkg from '../package.json' with { type: "json" }; import * as main from "../dist/node.js"; import meow from "meow"; @@ -7,9 +8,13 @@ const cli = meow( ` Usage $ tact --config + + or + + $ tact Options - --project , -p + --project , -p Build the specified project from the config file --version Prints the current Tact version --func Outputs the intermediate FunC code and stops --check Performs type checking and stops @@ -17,30 +22,41 @@ Options Examples $ tact --version - 1.2.0 + ${pkg.version} `, { importMeta: import.meta, flags: { - config: { shortFlags: "c", type: "string", isRequired: true }, + config: { shortFlags: "c", type: "string" }, project: { shortFlag: "p", type: "string", isMultiple: true }, version: { shortflag: "v", type: "boolean" }, - check: { shortflag: "c", type: "boolean" }, - func: { shortFlag: "f", type: "boolean" }, + check: { type: "boolean" }, + func: { type: "boolean" }, }, }, ); +if (cli.input.length > 1) { + console.log('Only one contract can be specified at a time'); + cli.showHelp(); +} + +if (cli.input.length == 1 && cli.flags.config) { + console.log('Either --config or a single Tact contract can be specified'); + cli.showHelp(); +} + if (cli.flags.version) { cli.showVersion(); } if (cli.flags.check && cli.flags.func) { console.log("--func and --check are mutually exclusive"); - process.exit(30); + cli.showHelp(); } const success = await main.run({ + fileName: cli.input.at(0), configPath: cli.flags.config, projectNames: cli.flags.project ?? [], checkOnly: cli.flags.check, diff --git a/package.json b/package.json index 9c6bd6f2a..8071bc056 100644 --- a/package.json +++ b/package.json @@ -91,5 +91,6 @@ "filename": "CHANGELOG.md" } } - } + }, + "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" } diff --git a/src/config/parseConfig.ts b/src/config/parseConfig.ts index 0d48645f7..f5eee252c 100644 --- a/src/config/parseConfig.ts +++ b/src/config/parseConfig.ts @@ -27,6 +27,9 @@ const configSchema = z .object({ $schema: z.string().optional(), projects: z.array(projectSchema), + checkOnly: z.boolean().optional(), + func: z.boolean().optional(), + rootPath: z.string().optional(), }) .strict(); diff --git a/src/node.ts b/src/node.ts index a22edbfcd..04691369c 100644 --- a/src/node.ts +++ b/src/node.ts @@ -5,25 +5,52 @@ import { createNodeFileSystem } from "./vfs/createNodeFileSystem"; import { build } from "./pipeline/build"; import { consoleLogger } from "./logger"; -export async function run(args: { - configPath: string; - projectNames?: string[]; - checkOnly?: boolean; - func?: boolean; -}) { +async function configForSingleFile(fileName: string): Promise { + return { + projects: [{ + name: "main", + path: fileName, + output: process.cwd(), + }], + rootPath: process.cwd(), + }; +} + +async function loadConfig(fileName?: string, configPath?: string): Promise { + if (fileName) + return configForSingleFile(fileName); + + if (!configPath) + return null; + + let config: Config; + // Load config - const resolvedPath = path.resolve(args.configPath); + const resolvedPath = path.resolve(configPath); const rootPath = path.dirname(resolvedPath); - let config: Config; if (!fs.existsSync(resolvedPath)) { console.warn("Unable to find config file at " + resolvedPath); - return false; + return null; } try { config = parseConfig(fs.readFileSync(resolvedPath, "utf8")); } catch (e) { console.log(e); console.warn("Unable to parse config file at " + resolvedPath); + return null; + } + return {rootPath, ...config}; +} + +export async function run(args: { + fileName?: string; + configPath?: string; + projectNames?: string[]; + checkOnly?: boolean; + func?: boolean; +}) { + const config = await loadConfig(args.fileName, args.configPath); + if (!config) { return false; } @@ -48,7 +75,7 @@ export async function run(args: { // Compile let success = true; - const project = createNodeFileSystem(rootPath, false); + const project = createNodeFileSystem(config.rootPath as string, false); const stdlib = createNodeFileSystem( path.resolve(__dirname, "..", "stdlib"), false, From f0ed93a5a5868581e6bea678b10c73ead2183d4c Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:54:39 +0200 Subject: [PATCH 15/37] fix: refactor cli args into the config object --- bin/tact.mjs | 8 +++++--- src/config/parseConfig.ts | 4 ++-- src/node.ts | 14 +++++++++----- src/pipeline/build.ts | 8 +++----- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/bin/tact.mjs b/bin/tact.mjs index 988eacd3d..7c29aee82 100755 --- a/bin/tact.mjs +++ b/bin/tact.mjs @@ -29,7 +29,7 @@ Examples flags: { config: { shortFlags: "c", type: "string" }, project: { shortFlag: "p", type: "string", isMultiple: true }, - version: { shortflag: "v", type: "boolean" }, + version: { shortFlag: "v", type: "boolean" }, check: { type: "boolean" }, func: { type: "boolean" }, }, @@ -59,8 +59,10 @@ const success = await main.run({ fileName: cli.input.at(0), configPath: cli.flags.config, projectNames: cli.flags.project ?? [], - checkOnly: cli.flags.check, - func: cli.flags.func, + cliOptions: { + checkOnly: cli.flags.check, + func: cli.flags.func, + }, }); process.exit(success ? 0 : 30); diff --git a/src/config/parseConfig.ts b/src/config/parseConfig.ts index f5eee252c..30fee2ae8 100644 --- a/src/config/parseConfig.ts +++ b/src/config/parseConfig.ts @@ -20,6 +20,8 @@ const projectSchema = z path: z.string(), output: z.string(), options: optionsSchema.optional(), + checkOnly: z.boolean().optional(), + func: z.boolean().optional(), }) .strict(); @@ -27,8 +29,6 @@ const configSchema = z .object({ $schema: z.string().optional(), projects: z.array(projectSchema), - checkOnly: z.boolean().optional(), - func: z.boolean().optional(), rootPath: z.string().optional(), }) .strict(); diff --git a/src/node.ts b/src/node.ts index 04691369c..c5ddccf31 100644 --- a/src/node.ts +++ b/src/node.ts @@ -5,12 +5,18 @@ import { createNodeFileSystem } from "./vfs/createNodeFileSystem"; import { build } from "./pipeline/build"; import { consoleLogger } from "./logger"; +export class CliOptions { + public checkOnly: boolean = false; + public func: boolean = false; +} + async function configForSingleFile(fileName: string): Promise { return { projects: [{ name: "main", path: fileName, output: process.cwd(), + options: { debug: true }, }], rootPath: process.cwd(), }; @@ -46,8 +52,7 @@ export async function run(args: { fileName?: string; configPath?: string; projectNames?: string[]; - checkOnly?: boolean; - func?: boolean; + cliOptions?: CliOptions; }) { const config = await loadConfig(args.fileName, args.configPath); if (!config) { @@ -82,13 +87,12 @@ export async function run(args: { ); // Improves developer experience for (const config of projects) { console.log("💼 Compiling project " + config.name + "..."); + const configAndOptions = {...config, ...args.cliOptions}; const built = await build({ - config, + config: configAndOptions, project, stdlib, logger: consoleLogger, - checkOnly: args.checkOnly, - func: args.func, }); success = success && built; } diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index 9f00e0bf7..72bdcb9b6 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -26,8 +26,6 @@ export async function build(args: { project: VirtualFileSystem; stdlib: string | VirtualFileSystem; logger?: TactLogger | null | undefined; - checkOnly?: boolean; - func?: boolean; }) { const { config, project } = args; const stdlib = @@ -70,7 +68,7 @@ export async function build(args: { return false; } - if (args.checkOnly) { + if (args.config.checkOnly) { logger.log("✔️ Type checking succeeded."); return true; } @@ -134,7 +132,7 @@ export async function build(args: { continue; } - if (args.func) { + if (args.config.func) { continue; } @@ -209,7 +207,7 @@ export async function build(args: { return false; } - if (args.func) { + if (args.config.func) { logger.log("✔️ FunC code generation succeeded."); return true; } From 6fe29ea7fae9fbc991945dceeaf8bf5384a571f5 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:09:57 +0200 Subject: [PATCH 16/37] chore: run prettier --- .github/workflows/tact.yml | 2 +- bin/tact.mjs | 6 +++--- src/node.ts | 31 +++++++++++++++++-------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 70c5819b4..8cbb14bee 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -53,7 +53,7 @@ jobs: if: runner.os == 'Windows' run: | type examples\output\echo_Echo.pkg - + - name: Install the package locally if: runner.os == 'Windows' run: | diff --git a/bin/tact.mjs b/bin/tact.mjs index 7c29aee82..e3dca6ee8 100755 --- a/bin/tact.mjs +++ b/bin/tact.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import pkg from '../package.json' with { type: "json" }; +import pkg from "../package.json" with { type: "json" }; import * as main from "../dist/node.js"; import meow from "meow"; @@ -37,12 +37,12 @@ Examples ); if (cli.input.length > 1) { - console.log('Only one contract can be specified at a time'); + console.log("Only one contract can be specified at a time"); cli.showHelp(); } if (cli.input.length == 1 && cli.flags.config) { - console.log('Either --config or a single Tact contract can be specified'); + console.log("Either --config or a single Tact contract can be specified"); cli.showHelp(); } diff --git a/src/node.ts b/src/node.ts index c5ddccf31..1125ef157 100644 --- a/src/node.ts +++ b/src/node.ts @@ -12,22 +12,25 @@ export class CliOptions { async function configForSingleFile(fileName: string): Promise { return { - projects: [{ - name: "main", - path: fileName, - output: process.cwd(), - options: { debug: true }, - }], + projects: [ + { + name: "main", + path: fileName, + output: process.cwd(), + options: { debug: true }, + }, + ], rootPath: process.cwd(), - }; + }; } -async function loadConfig(fileName?: string, configPath?: string): Promise { - if (fileName) - return configForSingleFile(fileName); +async function loadConfig( + fileName?: string, + configPath?: string, +): Promise { + if (fileName) return configForSingleFile(fileName); - if (!configPath) - return null; + if (!configPath) return null; let config: Config; @@ -45,7 +48,7 @@ async function loadConfig(fileName?: string, configPath?: string): Promise Date: Mon, 29 Apr 2024 10:27:04 +0200 Subject: [PATCH 17/37] feat: add tests. --- .github/workflows/tact.yml | 16 ++++++++++++++-- test/tact-cli/hello.tact | 7 +++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 test/tact-cli/hello.tact diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 8cbb14bee..5499e05b1 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -28,7 +28,10 @@ jobs: cache: "yarn" - name: Install dependencies - run: yarn install + + run: | + corepack enable + yarn install - name: Build and Test Tact compiler run: | @@ -55,7 +58,6 @@ jobs: type examples\output\echo_Echo.pkg - name: Install the package locally - if: runner.os == 'Windows' run: | npm install -g ./ @@ -66,6 +68,16 @@ jobs: then false fi + - name: Check command line options + run: | + tact --check test/tact-cli/hello.tact + tact --func test/tact-cli/hello.tact + tact test/tact-cli/hello.tact + + - name: Check command line arguments parsing + run: | + ! tact --nonexistentoption test/tact-cli/tact.config.json + - name: Tact CLI non-zero exit code if: runner.os != 'Windows' run: | diff --git a/test/tact-cli/hello.tact b/test/tact-cli/hello.tact new file mode 100644 index 000000000..2dd34b0de --- /dev/null +++ b/test/tact-cli/hello.tact @@ -0,0 +1,7 @@ +contract HelloWorld { + + get fun greeting(): String { + return "hello world"; + } + +} From 3a139b2415c3eeedf8b65afd678bf381061698cd Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:02:49 +0200 Subject: [PATCH 18/37] chore: run prettier --- .github/workflows/tact.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 5499e05b1..c9174e9c7 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -28,7 +28,7 @@ jobs: cache: "yarn" - name: Install dependencies - + run: | corepack enable yarn install From 15317255d21d69e7b8bdba4845072d9ce9c33134 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:22:02 +0200 Subject: [PATCH 19/37] fix: bin/tact for Blueprint compatibility --- bin/tact | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 bin/tact diff --git a/bin/tact b/bin/tact new file mode 100755 index 000000000..320c5c4b7 --- /dev/null +++ b/bin/tact @@ -0,0 +1,4 @@ +#!/bin/sh + +DIRNAME=$(dirname $0); +$DIRNAME/tact.mjs $@ \ No newline at end of file From df6813e1aae46c042b10abec0876e47fda3e9297 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:32:45 +0200 Subject: [PATCH 20/37] fix: handle symlinks in the bin/tact wrapper script --- bin/tact | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/tact b/bin/tact index 320c5c4b7..5342a7fde 100755 --- a/bin/tact +++ b/bin/tact @@ -1,4 +1,5 @@ #!/bin/sh -DIRNAME=$(dirname $0); +ABSOLUTE=$(readlink -e $0) +DIRNAME="$(dirname ${ABSOLUTE})" $DIRNAME/tact.mjs $@ \ No newline at end of file From ae2c5f64374f79493dfed7233d3e6648f8598e35 Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:44:49 +0200 Subject: [PATCH 21/37] fix: handle symlinks in macos, avoid negations on the windows flow --- .github/workflows/tact.yml | 1 + bin/tact | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index c9174e9c7..f4fa46046 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -75,6 +75,7 @@ jobs: tact test/tact-cli/hello.tact - name: Check command line arguments parsing + if: runner.os != 'Windows' run: | ! tact --nonexistentoption test/tact-cli/tact.config.json diff --git a/bin/tact b/bin/tact index 5342a7fde..bd8e7500c 100755 --- a/bin/tact +++ b/bin/tact @@ -1,5 +1,5 @@ #!/bin/sh -ABSOLUTE=$(readlink -e $0) +ABSOLUTE=$(readlink -f $0) DIRNAME="$(dirname ${ABSOLUTE})" $DIRNAME/tact.mjs $@ \ No newline at end of file From 7a025919b21a0201382b4b516b6b000eeb51453b Mon Sep 17 00:00:00 2001 From: Vitor Py Braga <12871+vitorpy@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:10:34 +0200 Subject: [PATCH 22/37] fix: rename bin tact.mjs to tact to avoid issues on windows --- bin/tact | 75 +++++++++++++++++++++++++++++++++++++++++++++++++--- bin/tact.mjs | 68 ----------------------------------------------- package.json | 5 ++-- 3 files changed, 73 insertions(+), 75 deletions(-) delete mode 100755 bin/tact.mjs diff --git a/bin/tact b/bin/tact index bd8e7500c..e0367672a 100755 --- a/bin/tact +++ b/bin/tact @@ -1,5 +1,72 @@ -#!/bin/sh +#!/usr/bin/env node -ABSOLUTE=$(readlink -f $0) -DIRNAME="$(dirname ${ABSOLUTE})" -$DIRNAME/tact.mjs $@ \ No newline at end of file +const pkg = require("../package.json"); +const main = require("../dist/node.js"); +const meowModule = import("meow"); + +meowModule.then((meow) => { + const cli = meow.default( + ` + Usage + $ tact --config + + or + + $ tact + + Options + --project , -p Build the specified project from the config file + --version Prints the current Tact version + --func Outputs the intermediate FunC code and stops + --check Performs type checking and stops + --help Displays this text + + Examples + $ tact --version + ${pkg.version} + `, + { + importMeta: { url: new URL("file://" + __dirname + __filename) }, + flags: { + config: { shortFlags: "c", type: "string" }, + project: { shortFlag: "p", type: "string", isMultiple: true }, + version: { shortFlag: "v", type: "boolean" }, + check: { type: "boolean" }, + func: { type: "boolean" }, + }, + }, + ); + + if (cli.input.length > 1) { + console.log("Only one contract can be specified at a time"); + cli.showHelp(); + } + + if (cli.input.length == 1 && cli.flags.config) { + console.log("Either --config or a single Tact contract can be specified"); + cli.showHelp(); + } + + if (cli.flags.version) { + cli.showVersion(); + } + + if (cli.flags.check && cli.flags.func) { + console.log("--func and --check are mutually exclusive"); + cli.showHelp(); + } + + main + .run({ + fileName: cli.input.at(0), + configPath: cli.flags.config, + projectNames: cli.flags.project ?? [], + cliOptions: { + checkOnly: cli.flags.check, + func: cli.flags.func, + }, + }) + .then((success) => { + process.exit(success ? 0 : 30); + }); +}); diff --git a/bin/tact.mjs b/bin/tact.mjs deleted file mode 100755 index e3dca6ee8..000000000 --- a/bin/tact.mjs +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env node - -import pkg from "../package.json" with { type: "json" }; -import * as main from "../dist/node.js"; -import meow from "meow"; - -const cli = meow( - ` -Usage - $ tact --config - - or - - $ tact - -Options - --project , -p Build the specified project from the config file - --version Prints the current Tact version - --func Outputs the intermediate FunC code and stops - --check Performs type checking and stops - --help Displays this text - -Examples - $ tact --version - ${pkg.version} -`, - { - importMeta: import.meta, - flags: { - config: { shortFlags: "c", type: "string" }, - project: { shortFlag: "p", type: "string", isMultiple: true }, - version: { shortFlag: "v", type: "boolean" }, - check: { type: "boolean" }, - func: { type: "boolean" }, - }, - }, -); - -if (cli.input.length > 1) { - console.log("Only one contract can be specified at a time"); - cli.showHelp(); -} - -if (cli.input.length == 1 && cli.flags.config) { - console.log("Either --config or a single Tact contract can be specified"); - cli.showHelp(); -} - -if (cli.flags.version) { - cli.showVersion(); -} - -if (cli.flags.check && cli.flags.func) { - console.log("--func and --check are mutually exclusive"); - cli.showHelp(); -} - -const success = await main.run({ - fileName: cli.input.at(0), - configPath: cli.flags.config, - projectNames: cli.flags.project ?? [], - cliOptions: { - checkOnly: cli.flags.check, - func: cli.flags.func, - }, -}); - -process.exit(success ? 0 : 30); diff --git a/package.json b/package.json index 8071bc056..71b0649dd 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ ], "main": "./dist/main.js", "bin": { - "tact": "./bin/tact.mjs" + "tact": "./bin/tact" }, "dependencies": { "@ipld/dag-pb": "2.1.18", @@ -91,6 +91,5 @@ "filename": "CHANGELOG.md" } } - }, - "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" + } } From dffe19304322a7ee3daf0b6fabf03c5010ff9dc5 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:44:08 +0200 Subject: [PATCH 23/37] fix/feat: debugged and enhanced the CLI --- bin/tact | 163 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 54 deletions(-) diff --git a/bin/tact b/bin/tact index e0367672a..4504d7c6e 100755 --- a/bin/tact +++ b/bin/tact @@ -1,72 +1,127 @@ #!/usr/bin/env node +// @ts-nocheck const pkg = require("../package.json"); const main = require("../dist/node.js"); const meowModule = import("meow"); -meowModule.then((meow) => { - const cli = meow.default( - ` +meowModule.then( + /** @param meow {import('meow/build/index')} */ + (meow) => { + const cli = meow.default( + ` Usage - $ tact --config - - or + $ tact [...flags] (--config CONFIG | FILE) - $ tact - - Options - --project , -p Build the specified project from the config file - --version Prints the current Tact version - --func Outputs the intermediate FunC code and stops - --check Performs type checking and stops - --help Displays this text + Flags + -c, --config CONFIG Specify path to config file (tact.config.json) + -p, --project ...names Build only the specified project(s) from the config file + --func Output intermediate FunC code and exit + --check Perform type checking and exit + -v, --version Print Tact compiler version and exit + -h, --help Display this text and exit Examples $ tact --version ${pkg.version} - `, - { - importMeta: { url: new URL("file://" + __dirname + __filename) }, - flags: { - config: { shortFlags: "c", type: "string" }, - project: { shortFlag: "p", type: "string", isMultiple: true }, - version: { shortFlag: "v", type: "boolean" }, - check: { type: "boolean" }, - func: { type: "boolean" }, + + Learn more about Tact: https://docs.tact-lang.org + Join Telegram group: https://t.me/tactlang + Follow X/Twitter account: https://twitter.com/tact_language`, + { + importMeta: { + url: new URL("file://" + __dirname + __filename).toString(), + }, + description: "A command-line utility for accessing the Tact compiler", + flags: { + config: { + shortFlag: "c", + type: "string", + isRequired: (flags, _) => { + // Require a config when the projects are specified AND version/help are not specified + if ( + flags.projects.length !== 0 && + !flags.version && + !flags.help + ) { + return true; + } + // Don't require it otherwise + return false; + }, + }, + projects: { shortFlag: "p", type: "string", isMultiple: true }, + func: { type: "boolean", default: false }, + check: { type: "boolean", default: false }, + version: { shortFlag: "v", type: "boolean" }, + help: { shortFlag: "h", type: "boolean" }, + }, + allowUnknownFlags: false, }, - }, - ); + ); - if (cli.input.length > 1) { - console.log("Only one contract can be specified at a time"); - cli.showHelp(); - } + // Show help regardless of other flags + if (cli.flags.help) { + cli.showHelp(0); + } - if (cli.input.length == 1 && cli.flags.config) { - console.log("Either --config or a single Tact contract can be specified"); - cli.showHelp(); - } + // Show version regardless of other flags + if (cli.flags.version) { + cli.showVersion(); + } - if (cli.flags.version) { - cli.showVersion(); - } + // Disallow specifying both config or Tact source file at the same time + if (cli.flags.config?.length !== 0 && cli.input.length > 0) { + console.log( + "Error: Both config and Tact file can't be simultaneously specified, pick one!", + ); + cli.showHelp(); + } - if (cli.flags.check && cli.flags.func) { - console.log("--func and --check are mutually exclusive"); - cli.showHelp(); - } + // Disallow empty config files + if (cli.flags.config?.length === 0 && cli.input.length === 0) { + console.log("Error: Config file wasn't specified!"); + cli.showHelp(); + } - main - .run({ - fileName: cli.input.at(0), - configPath: cli.flags.config, - projectNames: cli.flags.project ?? [], - cliOptions: { - checkOnly: cli.flags.check, - func: cli.flags.func, - }, - }) - .then((success) => { - process.exit(success ? 0 : 30); - }); -}); + // Disallow specifying both --func and --check flags at the same time + if (cli.flags.check && cli.flags.func) { + console.log("Error: Flags --func and --check are mutually exclusive!"); + cli.showHelp(); + } + + // Disallow specifying more than one Tact file + if (cli.input.length > 1) { + console.log( + "Error: Only one Tact file can be specified at a time. If you want more, provide a config!", + ); + cli.showHelp(); + } + + // Show help when all flags and inputs are empty + // Note, that version/help flags are already processed above and don't need to be mentioned here + if ( + cli.input.length === 0 && + cli.flags.check === false && + cli.flags.func === false && + cli.flags.projects.length === 0 && + cli.flags.config === undefined + ) { + cli.showHelp(0); + } + + main + .run({ + fileName: cli.input.at(0), + configPath: cli.flags.config, + projectNames: cli.flags.projects ?? [], + cliOptions: { + checkOnly: cli.flags.check, + func: cli.flags.func, + }, + }) + .then((success) => { + process.exit(success ? 0 : 30); + }); + }, +); From 8c083cb035f5130d90adb6082485134e3061a4c7 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:00:33 +0200 Subject: [PATCH 24/37] fix: debugged issues and simplified configurations --- bin/tact | 51 ++++++++++++++++++++++++--------------- src/config/parseConfig.ts | 3 +-- src/node.ts | 23 ++++++++++++------ src/pipeline/build.ts | 19 ++++++++++++--- 4 files changed, 64 insertions(+), 32 deletions(-) diff --git a/bin/tact b/bin/tact index 4504d7c6e..b7b588185 100755 --- a/bin/tact +++ b/bin/tact @@ -14,25 +14,25 @@ meowModule.then( $ tact [...flags] (--config CONFIG | FILE) Flags - -c, --config CONFIG Specify path to config file (tact.config.json) - -p, --project ...names Build only the specified project(s) from the config file - --func Output intermediate FunC code and exit - --check Perform type checking and exit - -v, --version Print Tact compiler version and exit - -h, --help Display this text and exit + -c, --config CONFIG Specify path to config file (tact.config.json) + -p, --project ...names Build only the specified project name(s) from the config file + --func Output intermediate FunC code and exit + --check Perform syntax and type checking, then exit + -v, --version Print Tact compiler version and exit + -h, --help Display this text and exit Examples $ tact --version ${pkg.version} - Learn more about Tact: https://docs.tact-lang.org - Join Telegram group: https://t.me/tactlang - Follow X/Twitter account: https://twitter.com/tact_language`, + Learn more about Tact: https://docs.tact-lang.org + Join Telegram group: https://t.me/tactlang + Follow X/Twitter account: https://twitter.com/tact_language`, { importMeta: { url: new URL("file://" + __dirname + __filename).toString(), }, - description: "A command-line utility for accessing the Tact compiler", + description: `Command-line utility wrapper for Tact compiler:\n${pkg.description}`, flags: { config: { shortFlag: "c", @@ -60,6 +60,14 @@ meowModule.then( }, ); + // Helper function to write less in following checks + const isEmptyConfigAndInput = () => { + if (cli.flags.config === undefined && cli.input.length === 0) { + return true; + } + return false; + }; + // Show help regardless of other flags if (cli.flags.help) { cli.showHelp(0); @@ -71,15 +79,15 @@ meowModule.then( } // Disallow specifying both config or Tact source file at the same time - if (cli.flags.config?.length !== 0 && cli.input.length > 0) { + if (cli.flags.config !== undefined && cli.input.length > 0) { console.log( "Error: Both config and Tact file can't be simultaneously specified, pick one!", ); cli.showHelp(); } - // Disallow empty config files - if (cli.flags.config?.length === 0 && cli.input.length === 0) { + // Disallow empty config files when other inputs are empty + if (isEmptyConfigAndInput() && !cli.flags.func && !cli.flags.check) { console.log("Error: Config file wasn't specified!"); cli.showHelp(); } @@ -90,6 +98,12 @@ meowModule.then( cli.showHelp(); } + // Disallow running --func and --check flags without a config or a file specified + if (isEmptyConfigAndInput() && (cli.flags.check || cli.flags.func)) { + console.log("Error: Either config or Tact file have to be specified!"); + cli.showHelp(); + } + // Disallow specifying more than one Tact file if (cli.input.length > 1) { console.log( @@ -101,11 +115,10 @@ meowModule.then( // Show help when all flags and inputs are empty // Note, that version/help flags are already processed above and don't need to be mentioned here if ( - cli.input.length === 0 && - cli.flags.check === false && - cli.flags.func === false && - cli.flags.projects.length === 0 && - cli.flags.config === undefined + isEmptyConfigAndInput() && + !cli.flags.check && + !cli.flags.func && + cli.flags.projects.length === 0 ) { cli.showHelp(0); } @@ -117,7 +130,7 @@ meowModule.then( projectNames: cli.flags.projects ?? [], cliOptions: { checkOnly: cli.flags.check, - func: cli.flags.func, + funcOnly: cli.flags.func, }, }) .then((success) => { diff --git a/src/config/parseConfig.ts b/src/config/parseConfig.ts index 30fee2ae8..b47886d7c 100644 --- a/src/config/parseConfig.ts +++ b/src/config/parseConfig.ts @@ -21,7 +21,7 @@ const projectSchema = z output: z.string(), options: optionsSchema.optional(), checkOnly: z.boolean().optional(), - func: z.boolean().optional(), + funcOnly: z.boolean().optional(), }) .strict(); @@ -29,7 +29,6 @@ const configSchema = z .object({ $schema: z.string().optional(), projects: z.array(projectSchema), - rootPath: z.string().optional(), }) .strict(); diff --git a/src/node.ts b/src/node.ts index 1125ef157..bb2cc68bc 100644 --- a/src/node.ts +++ b/src/node.ts @@ -7,10 +7,16 @@ import { consoleLogger } from "./logger"; export class CliOptions { public checkOnly: boolean = false; - public func: boolean = false; + public funcOnly: boolean = false; } -async function configForSingleFile(fileName: string): Promise { +type ConfigWithRootPath = Config & { + rootPath: string; +}; + +async function configForSingleFile( + fileName: string, +): Promise { return { projects: [ { @@ -27,7 +33,7 @@ async function configForSingleFile(fileName: string): Promise { async function loadConfig( fileName?: string, configPath?: string, -): Promise { +): Promise { if (fileName) return configForSingleFile(fileName); if (!configPath) return null; @@ -57,13 +63,13 @@ export async function run(args: { projectNames?: string[]; cliOptions?: CliOptions; }) { - const config = await loadConfig(args.fileName, args.configPath); - if (!config) { + const configWithRootPath = await loadConfig(args.fileName, args.configPath); + if (!configWithRootPath) { return false; } // Resolve projects - let projects = config.projects; + let projects = configWithRootPath.projects; if (args.projectNames && args.projectNames.length > 0) { // Check that all project names are valid for (const pp of args.projectNames) { @@ -83,7 +89,10 @@ export async function run(args: { // Compile let success = true; - const project = createNodeFileSystem(config.rootPath as string, false); + const project = createNodeFileSystem( + configWithRootPath.rootPath as string, + false, + ); const stdlib = createNodeFileSystem( path.resolve(__dirname, "..", "stdlib"), false, diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index 72bdcb9b6..c3ce863d0 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -63,13 +63,24 @@ export async function build(args: { try { ctx = precompile(ctx, project, stdlib, config.path); } catch (e) { - logger.error("Tact compilation failed"); + logger.error( + args.config.checkOnly || args.config.funcOnly + ? "Syntax and type checking failed" + : "Tact compilation failed", + ); logger.error(errorToString(e)); return false; } + if (args.config.checkOnly && args.config.funcOnly) { + logger.error( + "Checking is mutually exclusive with outputting only FunC code. Specify only one of those options in your tact.config.json!", + ); + return false; + } + if (args.config.checkOnly) { - logger.log("✔️ Type checking succeeded."); + logger.log("✔️ Syntax and type checking succeeded."); return true; } @@ -132,7 +143,7 @@ export async function build(args: { continue; } - if (args.config.func) { + if (args.config.funcOnly) { continue; } @@ -207,7 +218,7 @@ export async function build(args: { return false; } - if (args.config.func) { + if (args.config.funcOnly) { logger.log("✔️ FunC code generation succeeded."); return true; } From 29176882cab9ebe6c638fa7d5de7dd816a9f2a77 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:00:51 +0200 Subject: [PATCH 25/37] feat: extended JSON Schema for tact.config.json --- grammar/configSchema.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grammar/configSchema.json b/grammar/configSchema.json index 5016c7b00..d2011a228 100644 --- a/grammar/configSchema.json +++ b/grammar/configSchema.json @@ -52,6 +52,14 @@ } } } + }, + "checkOnly": { + "type": "boolean", + "description": "False by default. If set to true, only performs syntax and type checking, preventing further compilation. Mutually exclusive with `funcOnly`." + }, + "funcOnly": { + "type": "boolean", + "description": "False by default. If set to true, only outputs intermediate FunC code, preventing further compilation. Mutually exclusive with `checkOnly`." } } } From d340daf96b78aad858f297cbabdae9f33b568053 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:02:50 +0200 Subject: [PATCH 26/37] fix: moved catching of empty config after `--config` flag onto the build step --- bin/tact | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bin/tact b/bin/tact index b7b588185..27e91adee 100755 --- a/bin/tact +++ b/bin/tact @@ -86,12 +86,6 @@ meowModule.then( cli.showHelp(); } - // Disallow empty config files when other inputs are empty - if (isEmptyConfigAndInput() && !cli.flags.func && !cli.flags.check) { - console.log("Error: Config file wasn't specified!"); - cli.showHelp(); - } - // Disallow specifying both --func and --check flags at the same time if (cli.flags.check && cli.flags.func) { console.log("Error: Flags --func and --check are mutually exclusive!"); From 4d2ea34a0f92d17c812d640ca761ecf32e352690 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:05:06 +0200 Subject: [PATCH 27/37] chore: better wording --- bin/tact | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tact b/bin/tact index 27e91adee..589c01e30 100755 --- a/bin/tact +++ b/bin/tact @@ -32,7 +32,7 @@ meowModule.then( importMeta: { url: new URL("file://" + __dirname + __filename).toString(), }, - description: `Command-line utility wrapper for Tact compiler:\n${pkg.description}`, + description: `Command-line utility for the Tact compiler:\n${pkg.description}`, flags: { config: { shortFlag: "c", From 3ccfb6f62a3d88a3cc560c1df8f75af1fbb24dbf Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 30 Apr 2024 22:20:44 +0200 Subject: [PATCH 28/37] chore: updated CHANGELOG --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 756d25051..b18a896a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,8 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `isEmpty` extension function for the `Map` type: PR [#266](https://github.com/tact-lang/tact/pull/266) - The `pow2` power function with base 2: PR [#267](https://github.com/tact-lang/tact/pull/267) - The `try` and `try-catch` statements: PR [#212](https://github.com/tact-lang/tact/pull/212) -- The `--func` and `--check` command-line flags: PR [#287](https://github.com/tact-lang/tact/pull/287) - The `del` method for the `Map` type: PR [#95](https://github.com/tact-lang/tact/pull/95) +- The `-h`/`--help`, `-v` (short for `--version`), `-p` (short for `--project`), `--func` (for only outputting FunC code) and `--check` (for only doing the syntax and type checking) command-line flags: PR [#287](https://github.com/tact-lang/tact/pull/287) ### Changed @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `|` instead of `+` for send mode flags because the bitwise OR operation is idempotent and hence safer: PR [#274](https://github.com/tact-lang/tact/pull/274) - Bumped the versions of `@ton/core` and `ohm-js` to the most recent ones: PR [#276](https://github.com/tact-lang/tact/pull/276) - Generated `.pkg`-files always use POSIX file paths (even on Windows): PR [# 300](https://github.com/tact-lang/tact/pull/300) +- The `-p`/`--project` flags now allow specifying more than one project name. Additionally, they also require a `--config` flag to be specified: PR [#287](https://github.com/tact-lang/tact/pull/287) +- Command-line interface now allows compiling a single Tact file directly, without specifying a config: PR [#287](https://github.com/tact-lang/tact/pull/287) ### Fixed @@ -50,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use the most recent version of the FunC standard library [`stdlib.fc`](https://github.com/ton-blockchain/ton/blob/4cfe1d1a96acf956e28e2bbc696a143489e23631/crypto/smartcont/stdlib.fc): PR [#283](https://github.com/tact-lang/tact/pull/283) - The WASM version of the FunC compiler has been updated to 0.4.4 and patched to work on larger contracts: PR [#297](https://github.com/tact-lang/tact/pull/297) - The `return`-statement reachability analysis: PR [#302](https://github.com/tact-lang/tact/pull/302) +- The `-v`/`--version` flags now automatically source the version from `package.json`: PR [#287](https://github.com/tact-lang/tact/pull/287) ## [1.2.0] - 2024-02-29 From 81c099ea43472c8c2e4dc9fa0c42647df37485fe Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 12:51:54 +0200 Subject: [PATCH 29/37] chore: remove redundant line from CHANGELOG --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b18a896a1..2ff7d9c3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use the most recent version of the FunC standard library [`stdlib.fc`](https://github.com/ton-blockchain/ton/blob/4cfe1d1a96acf956e28e2bbc696a143489e23631/crypto/smartcont/stdlib.fc): PR [#283](https://github.com/tact-lang/tact/pull/283) - The WASM version of the FunC compiler has been updated to 0.4.4 and patched to work on larger contracts: PR [#297](https://github.com/tact-lang/tact/pull/297) - The `return`-statement reachability analysis: PR [#302](https://github.com/tact-lang/tact/pull/302) -- The `-v`/`--version` flags now automatically source the version from `package.json`: PR [#287](https://github.com/tact-lang/tact/pull/287) ## [1.2.0] - 2024-02-29 From c38b42cc3b970d51ea9b2b852b00426110ed82a2 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 13:22:49 +0200 Subject: [PATCH 30/37] feat: Introduce an enum for the compilation mode over disjoint boolean values --- grammar/configSchema.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/grammar/configSchema.json b/grammar/configSchema.json index d2011a228..e0d230c28 100644 --- a/grammar/configSchema.json +++ b/grammar/configSchema.json @@ -53,13 +53,12 @@ } } }, - "checkOnly": { - "type": "boolean", - "description": "False by default. If set to true, only performs syntax and type checking, preventing further compilation. Mutually exclusive with `funcOnly`." - }, - "funcOnly": { - "type": "boolean", - "description": "False by default. If set to true, only outputs intermediate FunC code, preventing further compilation. Mutually exclusive with `checkOnly`." + "mode": { + "type": "string", + "default": "full", + "enum": ["full", "checkOnly", "funcOnly"], + "title": "Compilation mode of the project.", + "description": "Set to `full` by default, which runs the whole pipeline of the compilation and emits FunC code, BoC and various utility files, including wrappers for TypeScript.\nIf set to `checkOnly`, only performs syntax and type checking, preventing further compilation.\nIf set to `funcOnly`, only outputs intermediate FunC code, preventing further compilation." } } } From 7dbb3456be87e1d9d1ebf43da406ef70544f8de0 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 13:24:28 +0200 Subject: [PATCH 31/37] chore: Visible default values (only as a schema completion hints) --- grammar/configSchema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/grammar/configSchema.json b/grammar/configSchema.json index e0d230c28..b1f0455a4 100644 --- a/grammar/configSchema.json +++ b/grammar/configSchema.json @@ -31,14 +31,17 @@ "properties": { "debug": { "type": "boolean", + "default": false, "description": "False by default. If set to true, enables debug output of a contract and allows usage of `dump()` function, which is useful for debugging purposes. With this option enabled, the contract will report that it was compiled in debug mode using the supported_interfaces method.\n\nRead more on debugging Tact code: https://docs.tact-lang.org/book/debug." }, "masterchain": { "type": "boolean", + "default": false, "description": "False by default. If set to true, enables masterchain support.\n\nRead more about masterchain: https://docs.tact-lang.org/book/masterchain." }, "external": { "type": "boolean", + "default": false, "description": "False by default. If set to true, enables support of external message receivers.\n\nRead more about external message receivers: https://docs.tact-lang.org/book/external." }, "experimental": { @@ -47,6 +50,7 @@ "properties": { "inline": { "type": "boolean", + "default": false, "description": "False by default. If set to true, enables inlining of all functions in contracts. This can reduce gas usage at the cost of bigger contracts." } } From 93c7fe07be12fa874904156880d575563ac428c7 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 13:47:40 +0200 Subject: [PATCH 32/37] chore: misc. things --- bin/tact | 1 + src/node.ts | 2 ++ test/tact-cli/tact.config.json | 5 +++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/tact b/bin/tact index 589c01e30..6941fcd49 100755 --- a/bin/tact +++ b/bin/tact @@ -128,6 +128,7 @@ meowModule.then( }, }) .then((success) => { + // https://nodejs.org/docs/v20.12.1/api/process.html#exit-codes process.exit(success ? 0 : 30); }); }, diff --git a/src/node.ts b/src/node.ts index bb2cc68bc..1141651b6 100644 --- a/src/node.ts +++ b/src/node.ts @@ -6,6 +6,8 @@ import { build } from "./pipeline/build"; import { consoleLogger } from "./logger"; export class CliOptions { + // The following options are mutually exclusive. + // That's checked in the CLI before passing their values here. public checkOnly: boolean = false; public funcOnly: boolean = false; } diff --git a/test/tact-cli/tact.config.json b/test/tact-cli/tact.config.json index 45d2631be..64a950bb5 100644 --- a/test/tact-cli/tact.config.json +++ b/test/tact-cli/tact.config.json @@ -1,10 +1,11 @@ { - "$schema": "http://raw.githubusercontent.com/tact-lang/tact/main/grammar/configSchema.json", + "$schema": "../../grammar/configSchema.json", "projects": [ { "name": "fail", "path": "./fail.tact", - "output": "./output" + "output": "./output", + "mode": "checkOnly" } ] } From 0b18c8cbe0f3fd7a68bb3652a8ee15f8cd612e65 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 13:54:05 +0200 Subject: [PATCH 33/37] feat: Enum of strings for compilation `mode` over many disjoint boolean flags --- CHANGELOG.md | 1 + src/config/parseConfig.ts | 3 +-- src/pipeline/build.ts | 15 ++++----------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ff7d9c3a..443bce3e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `try` and `try-catch` statements: PR [#212](https://github.com/tact-lang/tact/pull/212) - The `del` method for the `Map` type: PR [#95](https://github.com/tact-lang/tact/pull/95) - The `-h`/`--help`, `-v` (short for `--version`), `-p` (short for `--project`), `--func` (for only outputting FunC code) and `--check` (for only doing the syntax and type checking) command-line flags: PR [#287](https://github.com/tact-lang/tact/pull/287) +- The `mode` enum in project properties of `tact.config.json` for specifying compilation mode: `full` (default), `funcOnly` (only outputs FunC code and exits), or `checkOnly` (only does the syntax and type checking, then exits): PR [#287](https://github.com/tact-lang/tact/pull/287) ### Changed diff --git a/src/config/parseConfig.ts b/src/config/parseConfig.ts index b47886d7c..d683556da 100644 --- a/src/config/parseConfig.ts +++ b/src/config/parseConfig.ts @@ -20,8 +20,7 @@ const projectSchema = z path: z.string(), output: z.string(), options: optionsSchema.optional(), - checkOnly: z.boolean().optional(), - funcOnly: z.boolean().optional(), + mode: z.enum(["full", "checkOnly", "funcOnly"]).optional(), }) .strict(); diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index c3ce863d0..cec0ed362 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -64,7 +64,7 @@ export async function build(args: { ctx = precompile(ctx, project, stdlib, config.path); } catch (e) { logger.error( - args.config.checkOnly || args.config.funcOnly + args.config.mode === "checkOnly" || args.config.mode === "funcOnly" ? "Syntax and type checking failed" : "Tact compilation failed", ); @@ -72,14 +72,7 @@ export async function build(args: { return false; } - if (args.config.checkOnly && args.config.funcOnly) { - logger.error( - "Checking is mutually exclusive with outputting only FunC code. Specify only one of those options in your tact.config.json!", - ); - return false; - } - - if (args.config.checkOnly) { + if (args.config.mode === "checkOnly") { logger.log("✔️ Syntax and type checking succeeded."); return true; } @@ -143,7 +136,7 @@ export async function build(args: { continue; } - if (args.config.funcOnly) { + if (args.config.mode === "funcOnly") { continue; } @@ -218,7 +211,7 @@ export async function build(args: { return false; } - if (args.config.funcOnly) { + if (args.config.mode === "funcOnly") { logger.log("✔️ FunC code generation succeeded."); return true; } From a0257f8db6b7e718b5392692fb643f4323996922 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 16:31:59 +0200 Subject: [PATCH 34/37] fix: Bug with single and non-single file usage Separated the two in a more distinct way to reduce possible errors --- bin/tact | 13 ++++++--- src/node.ts | 27 +++++++++++-------- .../{tact.config.json => fail.config.json} | 2 +- test/tact-cli/success.config.json | 11 ++++++++ test/tact-cli/{hello.tact => success.tact} | 0 5 files changed, 37 insertions(+), 16 deletions(-) rename test/tact-cli/{tact.config.json => fail.config.json} (82%) create mode 100644 test/tact-cli/success.config.json rename test/tact-cli/{hello.tact => success.tact} (100%) diff --git a/bin/tact b/bin/tact index 6941fcd49..764bbf818 100755 --- a/bin/tact +++ b/bin/tact @@ -117,15 +117,20 @@ meowModule.then( cli.showHelp(0); } + // Compilation mode + const mode = cli.flags.check + ? "checkOnly" + : cli.flags.func + ? "funcOnly" + : undefined; + + // Main command main .run({ fileName: cli.input.at(0), configPath: cli.flags.config, projectNames: cli.flags.projects ?? [], - cliOptions: { - checkOnly: cli.flags.check, - funcOnly: cli.flags.func, - }, + singleFileOptions: { mode }, }) .then((success) => { // https://nodejs.org/docs/v20.12.1/api/process.html#exit-codes diff --git a/src/node.ts b/src/node.ts index 1141651b6..38d311b55 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1,19 +1,17 @@ import path from "path"; import fs from "fs"; -import { Config, parseConfig } from "./config/parseConfig"; +import { ConfigProject, Config, parseConfig } from "./config/parseConfig"; import { createNodeFileSystem } from "./vfs/createNodeFileSystem"; import { build } from "./pipeline/build"; import { consoleLogger } from "./logger"; -export class CliOptions { - // The following options are mutually exclusive. - // That's checked in the CLI before passing their values here. - public checkOnly: boolean = false; - public funcOnly: boolean = false; -} +type SingleFileOptions = { + mode?: ConfigProject["mode"]; +}; type ConfigWithRootPath = Config & { rootPath: string; + singleFile: boolean; }; async function configForSingleFile( @@ -22,13 +20,15 @@ async function configForSingleFile( return { projects: [ { - name: "main", + name: path.basename(fileName, ".tact"), path: fileName, output: process.cwd(), options: { debug: true }, + mode: "full", }, ], rootPath: process.cwd(), + singleFile: true, }; } @@ -56,14 +56,14 @@ async function loadConfig( console.warn("Unable to parse config file at " + resolvedPath); return null; } - return { rootPath, ...config }; + return { singleFile: false, rootPath, ...config }; } export async function run(args: { fileName?: string; configPath?: string; projectNames?: string[]; - cliOptions?: CliOptions; + singleFileOptions?: SingleFileOptions; }) { const configWithRootPath = await loadConfig(args.fileName, args.configPath); if (!configWithRootPath) { @@ -101,7 +101,12 @@ export async function run(args: { ); // Improves developer experience for (const config of projects) { console.log("💼 Compiling project " + config.name + "..."); - const configAndOptions = { ...config, ...args.cliOptions }; + let configAndOptions = { ...config }; + + if (configWithRootPath.singleFile) { + configAndOptions = { ...config, ...args.singleFileOptions }; + } + const built = await build({ config: configAndOptions, project, diff --git a/test/tact-cli/tact.config.json b/test/tact-cli/fail.config.json similarity index 82% rename from test/tact-cli/tact.config.json rename to test/tact-cli/fail.config.json index 64a950bb5..0b64cf4a2 100644 --- a/test/tact-cli/tact.config.json +++ b/test/tact-cli/fail.config.json @@ -4,7 +4,7 @@ { "name": "fail", "path": "./fail.tact", - "output": "./output", + "output": "./fail_output", "mode": "checkOnly" } ] diff --git a/test/tact-cli/success.config.json b/test/tact-cli/success.config.json new file mode 100644 index 000000000..3f2d8df6e --- /dev/null +++ b/test/tact-cli/success.config.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../grammar/configSchema.json", + "projects": [ + { + "name": "success", + "path": "./success.tact", + "output": "./success_output", + "mode": "checkOnly" + } + ] +} diff --git a/test/tact-cli/hello.tact b/test/tact-cli/success.tact similarity index 100% rename from test/tact-cli/hello.tact rename to test/tact-cli/success.tact From b6f964455a09dfac77457cf553bc49b9fc70f25b Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 16:40:56 +0200 Subject: [PATCH 35/37] chore: Update workflows --- .github/workflows/tact.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index f4fa46046..6c86acc01 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -70,19 +70,19 @@ jobs: - name: Check command line options run: | - tact --check test/tact-cli/hello.tact - tact --func test/tact-cli/hello.tact - tact test/tact-cli/hello.tact + tact --check test/tact-cli/success.tact + tact --func test/tact-cli/success.tact + tact test/tact-cli/success.tact - name: Check command line arguments parsing if: runner.os != 'Windows' run: | - ! tact --nonexistentoption test/tact-cli/tact.config.json + ! tact --nonexistentoption test/tact-cli/success.config.json - name: Tact CLI non-zero exit code if: runner.os != 'Windows' run: | - ! tact --config test/tact-cli/tact.config.json + ! tact --config test/tact-cli/fail.config.json - name: Link Tact compiler run: | From dcb834645404882ba1d10e144ec26d4c4e803937 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Wed, 1 May 2024 19:22:24 +0200 Subject: [PATCH 36/37] Apply suggestions from code review Co-authored-by: Anton Trunov --- .github/workflows/tact.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 6c86acc01..a30d20b32 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -61,25 +61,25 @@ jobs: run: | npm install -g ./ - - name: Compare Tact version from CLI flag `--version` against package.json + - name: CLI Test: Compare Tact version from CLI flag `--version` against package.json if: runner.os != 'Windows' run: | if [ "$(tact --version)" != "$(jq -r '.version' < package.json)" ]; then false fi - - name: Check command line options + - name: CLI Test: Check single-contract compilation run: | tact --check test/tact-cli/success.tact tact --func test/tact-cli/success.tact tact test/tact-cli/success.tact - - name: Check command line arguments parsing + - name: CLI Test: Check parsing of a non-existing CLI flag if: runner.os != 'Windows' run: | ! tact --nonexistentoption test/tact-cli/success.config.json - - name: Tact CLI non-zero exit code + - name: CLI Test: tact executable return non-zero exit code if compilation fails if: runner.os != 'Windows' run: | ! tact --config test/tact-cli/fail.config.json From d21d038d159faec90ccba9b7fa9e6d8844ba2a1d Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Thu, 2 May 2024 16:29:57 +0200 Subject: [PATCH 37/37] fix: corrected precedence -- local flags override ones in config --- .github/workflows/tact.yml | 7 +++++++ bin/tact | 6 +++++- src/node.ts | 14 ++++++++------ src/pipeline/build.ts | 8 ++++---- test/tact-cli/success.config.json | 2 +- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index a30d20b32..b5f6a12da 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -74,6 +74,13 @@ jobs: tact --func test/tact-cli/success.tact tact test/tact-cli/success.tact + - name: CLI Test: Check compilation via `--config` + run: | + # should output complete results + tact --config test/tact-cli/success.config.json + # should only run the syntax and type checking + tact --config test/tact-cli/success.config.json --check + - name: CLI Test: Check parsing of a non-existing CLI flag if: runner.os != 'Windows' run: | diff --git a/bin/tact b/bin/tact index 764bbf818..eeee578f1 100755 --- a/bin/tact +++ b/bin/tact @@ -124,13 +124,17 @@ meowModule.then( ? "funcOnly" : undefined; + // TODO: all flags on the cli should take precendence over flags in the config + // Make a nice model for it in the src/node.ts instead of the current mess + // Consider making overwrites right here or something. + // Main command main .run({ fileName: cli.input.at(0), configPath: cli.flags.config, projectNames: cli.flags.projects ?? [], - singleFileOptions: { mode }, + additionalCliOptions: { mode }, }) .then((success) => { // https://nodejs.org/docs/v20.12.1/api/process.html#exit-codes diff --git a/src/node.ts b/src/node.ts index 38d311b55..96ad98982 100644 --- a/src/node.ts +++ b/src/node.ts @@ -5,7 +5,7 @@ import { createNodeFileSystem } from "./vfs/createNodeFileSystem"; import { build } from "./pipeline/build"; import { consoleLogger } from "./logger"; -type SingleFileOptions = { +type AdditionalCliOptions = { mode?: ConfigProject["mode"]; }; @@ -63,7 +63,7 @@ export async function run(args: { fileName?: string; configPath?: string; projectNames?: string[]; - singleFileOptions?: SingleFileOptions; + additionalCliOptions?: AdditionalCliOptions; }) { const configWithRootPath = await loadConfig(args.fileName, args.configPath); if (!configWithRootPath) { @@ -101,14 +101,16 @@ export async function run(args: { ); // Improves developer experience for (const config of projects) { console.log("💼 Compiling project " + config.name + "..."); - let configAndOptions = { ...config }; + let cliConfig = { ...config }; - if (configWithRootPath.singleFile) { - configAndOptions = { ...config, ...args.singleFileOptions }; + if (args.additionalCliOptions !== undefined + && args.additionalCliOptions.mode !== undefined + ) { + cliConfig = { ...config, ...args.additionalCliOptions }; } const built = await build({ - config: configAndOptions, + config: cliConfig, project, stdlib, logger: consoleLogger, diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index cec0ed362..bcbdd3c63 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -64,7 +64,7 @@ export async function build(args: { ctx = precompile(ctx, project, stdlib, config.path); } catch (e) { logger.error( - args.config.mode === "checkOnly" || args.config.mode === "funcOnly" + config.mode === "checkOnly" || config.mode === "funcOnly" ? "Syntax and type checking failed" : "Tact compilation failed", ); @@ -72,7 +72,7 @@ export async function build(args: { return false; } - if (args.config.mode === "checkOnly") { + if (config.mode === "checkOnly") { logger.log("✔️ Syntax and type checking succeeded."); return true; } @@ -136,7 +136,7 @@ export async function build(args: { continue; } - if (args.config.mode === "funcOnly") { + if (config.mode === "funcOnly") { continue; } @@ -211,7 +211,7 @@ export async function build(args: { return false; } - if (args.config.mode === "funcOnly") { + if (config.mode === "funcOnly") { logger.log("✔️ FunC code generation succeeded."); return true; } diff --git a/test/tact-cli/success.config.json b/test/tact-cli/success.config.json index 3f2d8df6e..bddd21430 100644 --- a/test/tact-cli/success.config.json +++ b/test/tact-cli/success.config.json @@ -5,7 +5,7 @@ "name": "success", "path": "./success.tact", "output": "./success_output", - "mode": "checkOnly" + "mode": "full" } ] }