diff --git a/CHANGELOG.md b/CHANGELOG.md index 845bff98a..4e8822ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `-e` / `--eval` CLI flags to evaluate constant Tact expressions: PR [#462](https://github.com/tact-lang/tact/pull/462) +- `-q` / `--quiet` CLI flags to suppress compiler log output: PR [#509](https://github.com/tact-lang/tact/pull/509) ### Changed @@ -35,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Error message for duplicate receiver definitions inherited from traits: PR [#519](https://github.com/tact-lang/tact/issues/519) - Usage of `initOf` inside of `init()` does not cause error `135` anymore: PR [#521](https://github.com/tact-lang/tact/issues/521) - Usage of `newAddress` with hash parts shorter than 64 hexadecimal digits does not cause constant evaluation error `Invalid address hash length` anymore: PR [#525](https://github.com/tact-lang/tact/pull/525) +- Introduced a streamlined error logger for compilation pipeline to support third-party tools: PR [#509](https://github.com/tact-lang/tact/pull/509) ## [1.4.0] - 2024-06-21 diff --git a/bin/tact b/bin/tact index f55ac6d1c..92515b43a 100755 --- a/bin/tact +++ b/bin/tact @@ -16,6 +16,7 @@ meowModule.then( Flags -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 + -q, --quiet Suppress compiler log output --with-decompilation Full compilation followed by decompilation of produced binary code --func Output intermediate FunC code and exit --check Perform syntax and type checking, then exit @@ -51,14 +52,12 @@ meowModule.then( ); }, }, - eval: { - shortFlag: "e", - type: "string", - }, projects: { shortFlag: "p", type: "string", isMultiple: true }, + quiet: { shortFlag: "q", type: "boolean", default: false }, withDecompilation: { type: "boolean", default: false }, func: { type: "boolean", default: false }, check: { type: "boolean", default: false }, + eval: { shortFlag: "e", type: "string" }, version: { shortFlag: "v", type: "boolean" }, help: { shortFlag: "h", type: "boolean" }, }, @@ -164,10 +163,11 @@ meowModule.then( configPath: cli.flags.config, projectNames: cli.flags.projects ?? [], additionalCliOptions: { mode }, + suppressLog: cli.flags.quiet, }) - .then((success) => { + .then((response) => { // https://nodejs.org/docs/v20.12.1/api/process.html#exit-codes - process.exit(success ? 0 : 30); + process.exit(response.ok ? 0 : 30); }); }, ); diff --git a/scripts/prepare.ts b/scripts/prepare.ts index a23697b8b..c1c35fecf 100644 --- a/scripts/prepare.ts +++ b/scripts/prepare.ts @@ -5,7 +5,7 @@ import { FuncCompilationResult, funcCompile } from "../src/func/funcCompile"; import path from "path"; import { glob } from "glob"; import { verify } from "../src/verify"; -import { consoleLogger } from "../src/logger"; +import { Logger } from "../src/logger"; import { __DANGER__disableVersionNumber } from "../src/pipeline/version"; // Read cases @@ -13,74 +13,87 @@ void (async () => { // Disable version number in packages __DANGER__disableVersionNumber(); - // Compile projects - if (!(await run({ configPath: __dirname + "/../tact.config.json" }))) { - console.error("Tact projects compilation failed"); - process.exit(1); - } + const logger = new Logger(); - // Verify projects - for (const pkgPath of glob.sync( - path.normalize( - path.resolve(__dirname, "..", "examples", "output", "*.pkg"), - ), - )) { - const res = await verify({ pkg: fs.readFileSync(pkgPath, "utf-8") }); - if (!res.ok) { - console.error("Failed to verify " + pkgPath + ": " + res.error); - process.exit(1); + try { + // Compile projects + const compileResult = await run({ + configPath: path.join(__dirname, "..", "tact.config.json"), + }); + if (!compileResult.ok) { + throw new Error("Tact projects compilation failed"); } - } - // Compile func files - for (const p of [{ path: __dirname + "/../func/" }]) { - const recs = fs.readdirSync(p.path); - for (const r of recs) { - if (!r.endsWith(".fc")) { - continue; + // Verify projects + for (const pkgPath of glob.sync( + path.normalize( + path.resolve(__dirname, "..", "examples", "output", "*.pkg"), + ), + )) { + const res = await verify({ + pkg: fs.readFileSync(pkgPath, "utf-8"), + }); + if (!res.ok) { + throw new Error(`Failed to verify ${pkgPath}: ${res.error}`); } + } - // Precompile - console.log("Processing " + p.path + r); - let c: FuncCompilationResult; - try { - const stdlibPath = path.resolve( - __dirname, - "..", - "stdlib", - "stdlib.fc", - ); - const stdlib = fs.readFileSync(stdlibPath, "utf-8"); - const code = fs.readFileSync(p.path + r, "utf-8"); - c = await funcCompile({ - entries: [stdlibPath, p.path + r], - sources: [ - { - path: stdlibPath, - content: stdlib, - }, - { - path: p.path + r, - content: code, - }, - ], - logger: consoleLogger, - }); - if (!c.ok) { - console.error(c.log); - process.exit(1); + // Compile func files + for (const p of [{ path: path.join(__dirname, "..", "func") }]) { + const files = fs.readdirSync(p.path); + for (const file of files) { + if (!file.endsWith(".fc")) { + continue; } - } catch (e) { - console.error(e); - console.error("Failed"); - process.exit(1); - } - fs.writeFileSync(p.path + r + ".fift", c.fift!); - fs.writeFileSync(p.path + r + ".cell", c.output!); - // Cell -> Fift decompiler - const source = decompileAll({ src: c.output! }); - fs.writeFileSync(p.path + r + ".rev.fift", source); + // Precompile + const funcFileFullPath = path.join(p.path, file); + logger.info(`Processing ${funcFileFullPath}`); + let c: FuncCompilationResult; + try { + const stdlibPath = path.resolve( + __dirname, + "..", + "stdlib", + "stdlib.fc", + ); + const stdlib = fs.readFileSync(stdlibPath, "utf-8"); + const code = fs.readFileSync(funcFileFullPath, "utf-8"); + c = await funcCompile({ + entries: [stdlibPath, funcFileFullPath], + sources: [ + { + path: stdlibPath, + content: stdlib, + }, + { + path: funcFileFullPath, + content: code, + }, + ], + logger: logger, + }); + if (!c.ok) { + logger.error(c.log); + throw new Error( + `FunC compilation failed for ${funcFileFullPath}`, + ); + } + } catch (e) { + logger.error(e as Error); + logger.error(`Failed for ${funcFileFullPath}`); + throw e; + } + fs.writeFileSync(funcFileFullPath + ".fift", c.fift!); + fs.writeFileSync(funcFileFullPath + ".cell", c.output!); + + // Cell -> Fift decompiler + const source = decompileAll({ src: c.output! }); + fs.writeFileSync(funcFileFullPath + ".rev.fift", source); + } } + } catch (error) { + logger.error(error as Error); + process.exit(1); } })(); diff --git a/src/browser.ts b/src/browser.ts index f6ce4cdd6..6878dbd4a 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -1,12 +1,12 @@ import { Config, verifyConfig } from "./config/parseConfig"; -import { TactLogger } from "./logger"; +import { Logger } from "./logger"; import { build } from "./pipeline/build"; import { createVirtualFileSystem } from "./vfs/createVirtualFileSystem"; export async function run(args: { config: Config; files: Record; - logger?: TactLogger | null | undefined; + logger?: Logger; }) { // Verify config const config = verifyConfig(args.config); @@ -19,6 +19,7 @@ export async function run(args: { // Compile let success = true; + let errorCollection: Error[] = []; for (const p of config.projects) { const built = await build({ config: p, @@ -26,7 +27,10 @@ export async function run(args: { stdlib, logger: args.logger, }); - success = success && built; + success = success && built.ok; + if (!built.ok) { + errorCollection = { ...errorCollection, ...built.error }; + } } - return success; + return { ok: success, error: errorCollection }; } diff --git a/src/errors.ts b/src/errors.ts index 2c7a476d9..c932552ba 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -104,3 +104,10 @@ export function idTextErr( } return `"${ident.text}"`; } + +export type TactErrorCollection = + | Error + | TactParseError + | TactCompilationError + | TactInternalCompilerError + | TactConstEvalError; diff --git a/src/func/funcCompile.spec.ts b/src/func/funcCompile.spec.ts index e8661f8af..60e937b10 100644 --- a/src/func/funcCompile.spec.ts +++ b/src/func/funcCompile.spec.ts @@ -1,6 +1,6 @@ import fs from "fs"; import path from "path"; -import { consoleLogger } from "../logger"; +import { Logger } from "../logger"; import { funcCompile } from "./funcCompile"; import files from "../imports/stdlib"; @@ -22,7 +22,7 @@ describe("funcCompile", () => { }, { path: "/small.fc", content: source }, ], - logger: consoleLogger, + logger: new Logger(), }); expect(res.ok).toBe(true); }); diff --git a/src/func/funcCompile.ts b/src/func/funcCompile.ts index 222593155..1bcb579ab 100644 --- a/src/func/funcCompile.ts +++ b/src/func/funcCompile.ts @@ -1,5 +1,4 @@ -import { TactLogger } from "../logger"; -import { errorToString } from "../utils/errorToString"; +import { Logger } from "../logger"; // Wasm Imports // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -60,7 +59,7 @@ type CompileResult = export async function funcCompile(args: { entries: string[]; sources: { path: string; content: string }[]; - logger: TactLogger; + logger: Logger; }): Promise { // Parameters const files: string[] = args.entries; @@ -181,7 +180,7 @@ export async function funcCompile(args: { } } } catch (e) { - args.logger.error(errorToString(e)); + args.logger.error(e as Error); throw Error("Unexpected compiler response"); } finally { for (const i of allocatedFunctions) { diff --git a/src/logger.ts b/src/logger.ts index f50888ee6..67f7bf198 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,9 +1,87 @@ -export type TactLogger = { - log: (message: string) => void; - error: (message: string) => void; -}; +export enum LogLevel { + /** Logging is turned off */ + NONE, + /** Logs only error messages */ + ERROR, + /** Logs warning and error messages */ + WARN, + /** Logs informational, warning, and error messages */ + INFO, + /** Logs debugging, informational, warning, and error messages */ + DEBUG, +} + +type messageType = string | Error; -export const consoleLogger: TactLogger = { - log: console.log, - error: console.error, +export interface LogMethods { + debug: (message: messageType) => void; + info: (message: messageType) => void; + warn: (message: messageType) => void; + error: (message: messageType) => void; +} + +const logLevelToMethodName: { [key in LogLevel]: keyof LogMethods | null } = { + [LogLevel.NONE]: null, + [LogLevel.ERROR]: "error", + [LogLevel.WARN]: "warn", + [LogLevel.INFO]: "info", + [LogLevel.DEBUG]: "debug", }; + +function getLoggingMethod(level: LogLevel): keyof LogMethods | null { + return logLevelToMethodName[level]; +} + +export class Logger { + private level: LogLevel; + private logMethods: LogMethods; + + constructor(level: LogLevel = LogLevel.INFO) { + this.level = level; + this.logMethods = { + debug: console.log, + info: console.log, + warn: console.warn, + error: console.error, + }; + } + + protected log(level: LogLevel, message: messageType) { + if (this.level === LogLevel.NONE) { + return; + } + + if (message instanceof Error) { + message = message.stack ?? message.message; + } else { + message = message.toString(); + } + + if (level > this.level) return; + + const loggingMethod = getLoggingMethod(level); + if (!loggingMethod) return; + + this.logMethods[loggingMethod](message); + } + + debug(message: messageType) { + this.log(LogLevel.DEBUG, message); + } + + info(message: messageType) { + this.log(LogLevel.INFO, message); + } + + warn(message: messageType) { + this.log(LogLevel.WARN, message); + } + + error(message: messageType) { + this.log(LogLevel.ERROR, message); + } + + setLevel(level: LogLevel) { + this.level = level; + } +} diff --git a/src/node.ts b/src/node.ts index 675687c82..170330913 100644 --- a/src/node.ts +++ b/src/node.ts @@ -3,7 +3,8 @@ import fs from "fs"; import { ConfigProject, Config, parseConfig } from "./config/parseConfig"; import { createNodeFileSystem } from "./vfs/createNodeFileSystem"; import { build } from "./pipeline/build"; -import { consoleLogger } from "./logger"; +import { LogLevel, Logger } from "./logger"; +import { TactErrorCollection } from "./errors"; type AdditionalCliOptions = { mode?: ConfigProject["mode"]; @@ -67,20 +68,34 @@ export async function run(args: { configPath?: string; projectNames?: string[]; additionalCliOptions?: AdditionalCliOptions; + suppressLog?: boolean; }) { const configWithRootPath = await loadConfig(args.fileName, args.configPath); if (!configWithRootPath) { - return false; + return { + ok: false, + error: [ + new Error( + `Unable to load config from path: ${args.configPath}`, + ), + ], + }; } + const logger = new Logger(args.suppressLog ? LogLevel.NONE : LogLevel.INFO); + // Resolve 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) { if (!projects.find((v) => v.name === pp)) { - console.warn("Unable to find project " + pp); - return false; + const message = "Unable to find project " + pp; + logger.warn(message); + return { + ok: false, + error: [new Error(message)], + }; } } @@ -88,12 +103,14 @@ export async function run(args: { projects = projects.filter((v) => args.projectNames!.includes(v.name)); } if (projects.length === 0) { - console.warn("No projects to compile"); - return false; + const message = "No projects to compile"; + console.warn(message); + return { ok: false, error: [new Error(message)] }; } // Compile let success = true; + let errorMessages: TactErrorCollection[] = []; const project = createNodeFileSystem( configWithRootPath.rootPath as string, false, @@ -103,7 +120,7 @@ export async function run(args: { false, ); // Improves developer experience for (const config of projects) { - consoleLogger.log("💼 Compiling project " + config.name + "..."); + logger.info(`💼 Compiling project ${config.name} ...`); let cliConfig = { ...config }; if (args.additionalCliOptions?.mode !== undefined) { @@ -114,11 +131,14 @@ export async function run(args: { config: cliConfig, project, stdlib, - logger: consoleLogger, + logger, }); - success = success && built; + success = success && built.ok; + if (!built.ok && built.error.length > 0) { + errorMessages = [...errorMessages, ...built.error]; + } } - return success; + return { ok: success, error: errorMessages }; } export { createNodeFileSystem } from "./vfs/createNodeFileSystem"; diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index ac12ea513..1e5bd8c85 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -8,12 +8,11 @@ import { funcCompile } from "../func/funcCompile"; import { writeReport } from "../generator/writeReport"; import { getRawAST } from "../grammar/store"; import files from "../imports/stdlib"; -import { consoleLogger, TactLogger } from "../logger"; +import { Logger } from "../logger"; import { PackageFileFormat } from "../packaging/fileFormat"; import { packageCode } from "../packaging/packageCode"; import { createABITypeRefFromTypeRef } from "../types/resolveABITypeRef"; import { getContracts, getType } from "../types/resolveDescriptors"; -import { errorToString } from "../utils/errorToString"; import { posixNormalize } from "../utils/filePath"; import { createVirtualFileSystem } from "../vfs/createVirtualFileSystem"; import { VirtualFileSystem } from "../vfs/VirtualFileSystem"; @@ -21,19 +20,20 @@ import { compile } from "./compile"; import { precompile } from "./precompile"; import { getCompilerVersion } from "./version"; import { idText } from "../grammar/ast"; +import { TactErrorCollection } from "../errors"; export async function build(args: { config: ConfigProject; project: VirtualFileSystem; stdlib: string | VirtualFileSystem; - logger?: TactLogger | null | undefined; -}) { + logger?: Logger; +}): Promise<{ ok: boolean; error: TactErrorCollection[] }> { const { config, project } = args; const stdlib = typeof args.stdlib === "string" ? createVirtualFileSystem(args.stdlib, files) : args.stdlib; - const logger: TactLogger = args.logger ?? consoleLogger; + const logger: Logger = args.logger ?? new Logger(); // Configure context let ctx: CompilerContext = new CompilerContext({ shared: {} }); @@ -77,17 +77,18 @@ export async function build(args: { ? "Syntax and type checking failed" : "Tact compilation failed", ); - logger.error(errorToString(e)); - return false; + logger.error(e as Error); + return { ok: false, error: [e as Error] }; } if (config.mode === "checkOnly") { - logger.log("✔️ Syntax and type checking succeeded."); - return true; + logger.info("✔️ Syntax and type checking succeeded."); + return { ok: true, error: [] }; } // Compile contracts let ok = true; + const errorMessages: TactErrorCollection[] = []; const built: Record< string, | { @@ -118,7 +119,7 @@ export async function build(args: { let codeEntrypoint: string; // Compiling contract to func - logger.log(" > " + contract + ": tact compiler"); + logger.info(` > ${contract}: tact compiler`); let abi: string; try { const res = await compile( @@ -139,8 +140,9 @@ export async function build(args: { codeEntrypoint = res.output.entrypoint; } catch (e) { logger.error("Tact compilation failed"); - logger.error(errorToString(e)); + logger.error(e as Error); ok = false; + errorMessages.push(e as Error); continue; } @@ -149,7 +151,7 @@ export async function build(args: { } // Compiling contract to TVM - logger.log(" > " + contract + ": func compiler"); + logger.info(` > ${contract}: func compiler`); let codeBoc: Buffer; try { const stdlibPath = stdlib.resolve("stdlib.fc"); @@ -180,6 +182,7 @@ export async function build(args: { if (!c.ok) { logger.error(c.log); ok = false; + errorMessages.push(new Error(c.log)); continue; } project.writeFile(pathCodeFif, c.fift); @@ -187,8 +190,9 @@ export async function build(args: { codeBoc = c.output; } catch (e) { logger.error("FunC compiler crashed"); - logger.error(errorToString(e)); + logger.error(e as Error); ok = false; + errorMessages.push(e as Error); continue; } @@ -200,39 +204,42 @@ export async function build(args: { if (config.mode === "fullWithDecompilation") { // Fift decompiler for generated code debug - logger.log(" > " + contract + ": fift decompiler"); + logger.info(` > ${contract}: fift decompiler`); let codeFiftDecompiled: string; try { codeFiftDecompiled = decompileAll({ src: codeBoc }); project.writeFile(pathCodeFifDec, codeFiftDecompiled); } catch (e) { logger.error("Fift decompiler crashed"); - logger.error(errorToString(e)); + logger.error(e as Error); ok = false; + errorMessages.push(e as Error); continue; } } } if (!ok) { - logger.log("💥 Compilation failed. Skipping packaging"); - return false; + logger.info("💥 Compilation failed. Skipping packaging"); + return { ok: false, error: errorMessages }; } if (config.mode === "funcOnly") { - logger.log("✔️ FunC code generation succeeded."); - return true; + logger.info("✔️ FunC code generation succeeded."); + return { ok: true, error: errorMessages }; } // Package - logger.log(" > Packaging"); + logger.info(" > Packaging"); const contracts = getContracts(ctx); const packages: PackageFileFormat[] = []; for (const contract of contracts) { - logger.log(" > " + contract); + logger.info(" > " + contract); const artifacts = built[contract]; if (!artifacts) { - logger.error(" > " + contract + ": no artifacts found"); - return false; + const message = ` > ${contract}: no artifacts found`; + logger.error(message); + errorMessages.push(new Error(message)); + return { ok: false, error: errorMessages }; } // System cell @@ -245,8 +252,10 @@ export async function build(args: { for (const c of ct.dependsOn) { const cd = built[c.name]; if (!cd) { - logger.error(` > ${c.name}: no artifacts found`); - return false; + const message = ` > ${c.name}: no artifacts found`; + logger.error(message); + errorMessages.push(new Error(message)); + return { ok: false, error: errorMessages }; } depends.set(c.uid, Cell.fromBoc(cd.codeBoc)[0]!); } @@ -306,17 +315,14 @@ export async function build(args: { } // Bindings - logger.log(" > Bindings"); + logger.info(" > Bindings"); for (const pkg of packages) { - logger.log(" > " + pkg.name); + logger.info(` > ${pkg.name}`); if (pkg.init.deployment.kind !== "system-cell") { - logger.error( - " > " + - pkg.name + - ": unsupported deployment kind " + - pkg.init.deployment.kind, - ); - return false; + const message = ` > ${pkg.name}: unsupported deployment kind ${pkg.init.deployment.kind}`; + logger.error(message); + errorMessages.push(new Error(message)); + return { ok: false, error: errorMessages }; } try { const bindingsServer = writeTypescript(JSON.parse(pkg.abi), { @@ -333,16 +339,18 @@ export async function build(args: { bindingsServer, ); } catch (e) { - logger.error("Bindings compiler crashed"); - logger.error(errorToString(e)); - return false; + const error = e as Error; + error.message = `Bindings compiler crashed: ${error.message}`; + logger.error(error); + errorMessages.push(error); + return { ok: false, error: errorMessages }; } } // Reports - logger.log(" > Reports"); + logger.info(" > Reports"); for (const pkg of packages) { - logger.log(" > " + pkg.name); + logger.info(" > " + pkg.name); try { const report = writeReport(ctx, pkg); const pathBindings = project.resolve( @@ -351,11 +359,13 @@ export async function build(args: { ); project.writeFile(pathBindings, report); } catch (e) { - logger.error("Report generation crashed"); - logger.error(errorToString(e)); - return false; + const error = e as Error; + error.message = `Report generation crashed: ${error.message}`; + logger.error(error); + errorMessages.push(error); + return { ok: false, error: errorMessages }; } } - return true; + return { ok: true, error: [] }; } diff --git a/src/test/compilation-failed/const-eval-failed.spec.ts b/src/test/compilation-failed/const-eval-failed.spec.ts index 887cda73d..ce1cebc0a 100644 --- a/src/test/compilation-failed/const-eval-failed.spec.ts +++ b/src/test/compilation-failed/const-eval-failed.spec.ts @@ -1,25 +1,11 @@ import { __DANGER_resetNodeId } from "../../grammar/ast"; -import { consoleLogger } from "../../logger"; import { itShouldNotCompile } from "./util"; describe("fail-const-eval", () => { - beforeAll(() => { - jest.spyOn(consoleLogger, "error").mockImplementation(() => {}); - jest.spyOn(consoleLogger, "log").mockImplementation(() => {}); - }); - beforeEach(() => { __DANGER_resetNodeId(); }); - afterAll(() => { - jest.restoreAllMocks(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - itShouldNotCompile({ testName: "const-eval-div-by-zero", errorMessage: diff --git a/src/test/compilation-failed/contract-duplicate-opcodes.spec.ts b/src/test/compilation-failed/contract-duplicate-opcodes.spec.ts index 98595eb4c..a799e930c 100644 --- a/src/test/compilation-failed/contract-duplicate-opcodes.spec.ts +++ b/src/test/compilation-failed/contract-duplicate-opcodes.spec.ts @@ -1,25 +1,11 @@ import { __DANGER_resetNodeId } from "../../grammar/ast"; -import { consoleLogger } from "../../logger"; import { itShouldNotCompile } from "./util"; describe("contract-duplicate-opcodes", () => { - beforeAll(() => { - jest.spyOn(consoleLogger, "error").mockImplementation(() => {}); - jest.spyOn(consoleLogger, "log").mockImplementation(() => {}); - }); - beforeEach(() => { __DANGER_resetNodeId(); }); - afterAll(() => { - jest.restoreAllMocks(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - itShouldNotCompile({ testName: "contract-duplicate-bounced-opcode", errorMessage: diff --git a/src/test/compilation-failed/stdlib-bugs.spec.ts b/src/test/compilation-failed/stdlib-bugs.spec.ts index 7659947e7..da8cc72e6 100644 --- a/src/test/compilation-failed/stdlib-bugs.spec.ts +++ b/src/test/compilation-failed/stdlib-bugs.spec.ts @@ -1,25 +1,11 @@ import { __DANGER_resetNodeId } from "../../grammar/ast"; -import { consoleLogger } from "../../logger"; import { itShouldNotCompile } from "./util"; describe("stdlib-bugs", () => { - beforeAll(() => { - jest.spyOn(consoleLogger, "error").mockImplementation(() => {}); - jest.spyOn(consoleLogger, "log").mockImplementation(() => {}); - }); - beforeEach(() => { __DANGER_resetNodeId(); }); - afterAll(() => { - jest.restoreAllMocks(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - itShouldNotCompile({ testName: "stdlib-skipBits", errorMessage: 'Type mismatch: "" is not assignable to "Slice"', diff --git a/src/test/compilation-failed/util.ts b/src/test/compilation-failed/util.ts index 48eb88ac1..2d893de24 100644 --- a/src/test/compilation-failed/util.ts +++ b/src/test/compilation-failed/util.ts @@ -1,5 +1,4 @@ import { run } from "../../node"; -import { consoleLogger } from "../../logger"; // helper to reduce boilerplate export function itShouldNotCompile(params: { @@ -10,10 +9,10 @@ export function itShouldNotCompile(params: { const result = await run({ configPath: __dirname + "/tact.config.json", projectNames: [params.testName], + suppressLog: true, }); - expect(result).toBe(false); - expect((consoleLogger.error as jest.Mock).mock.lastCall[0]).toContain( - params.errorMessage, - ); + expect(result.ok).toBe(false); + const message = result.error.map((err) => err.message).join("; "); + expect(message).toContain(params.errorMessage); }); } diff --git a/src/test/e2e-emulated/address.spec.ts b/src/test/e2e-emulated/address.spec.ts index dc19a42f0..9cd828551 100644 --- a/src/test/e2e-emulated/address.spec.ts +++ b/src/test/e2e-emulated/address.spec.ts @@ -2,24 +2,12 @@ import { toNano } from "@ton/core"; import { ContractSystem } from "@tact-lang/emulator"; import { __DANGER_resetNodeId } from "../../grammar/ast"; import { AddressTester } from "./contracts/output/address_AddressTester"; -import { consoleLogger } from "../../logger"; describe("address", () => { - beforeAll(() => { - jest.spyOn(consoleLogger, "error").mockImplementation(() => {}); - }); - beforeEach(() => { __DANGER_resetNodeId(); }); - afterAll(() => { - (consoleLogger.error as jest.Mock).mockRestore(); - }); - - afterEach(() => { - (consoleLogger.error as jest.Mock).mockClear(); - }); it("should implement addresses correctly", async () => { // Init const system = await ContractSystem.create(); diff --git a/src/test/e2e-emulated/initof.spec.ts b/src/test/e2e-emulated/initof.spec.ts index 3539aba15..fde91aad1 100644 --- a/src/test/e2e-emulated/initof.spec.ts +++ b/src/test/e2e-emulated/initof.spec.ts @@ -3,26 +3,14 @@ import { ContractSystem } from "@tact-lang/emulator"; import { __DANGER_resetNodeId } from "../../grammar/ast"; import { Self } from "./contracts/output/initof_Self"; import { Parent } from "./contracts/output/initof_Parent"; -import { consoleLogger } from "../../logger"; import { TestInit } from "./contracts/output/initof-2_TestInit"; import { A } from "./contracts/output/initof-3_A"; describe("initOf", () => { - beforeAll(() => { - jest.spyOn(consoleLogger, "error").mockImplementation(() => {}); - }); - beforeEach(() => { __DANGER_resetNodeId(); }); - afterAll(() => { - (consoleLogger.error as jest.Mock).mockRestore(); - }); - - afterEach(() => { - (consoleLogger.error as jest.Mock).mockClear(); - }); it("should implement initOf correctly - 1", async () => { // Init const system = await ContractSystem.create(); diff --git a/src/verify.ts b/src/verify.ts index b466d3d8c..020049690 100644 --- a/src/verify.ts +++ b/src/verify.ts @@ -1,8 +1,8 @@ import normalize from "path-normalize"; import { Cell } from "@ton/core"; import { Config, Options } from "./config/parseConfig"; -import { consoleLogger } from "./logger"; -import { PackageFileFormat, run, TactLogger } from "./main"; +import { Logger } from "./logger"; +import { PackageFileFormat, run } from "./main"; import { fileFormat } from "./packaging/fileFormat"; import { getCompilerVersion } from "./pipeline/version"; @@ -24,9 +24,9 @@ export type VerifyResult = export async function verify(args: { pkg: string; - logger?: TactLogger | null | undefined; + logger?: Logger | null | undefined; }): Promise { - const logger = args.logger ?? consoleLogger; + const logger = args.logger ?? new Logger(); // Loading package let unpacked: PackageFileFormat; @@ -79,7 +79,7 @@ export async function verify(args: { } const result = await run({ config, files, logger }); - if (!result) { + if (!result.ok) { return { ok: false, error: "compilation-failed" }; }