Skip to content

Commit

Permalink
refactor: generate zod schema from types (#1798)
Browse files Browse the repository at this point in the history
  • Loading branch information
verytactical authored Feb 12, 2025
1 parent 081cbc5 commit 93c6fba
Show file tree
Hide file tree
Showing 10 changed files with 763 additions and 214 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author": "Steve Korshakov <[email protected]>",
"license": "MIT",
"scripts": {
"gen:config": "ts-to-zod -k --skipValidation src/config/config.ts src/config/config.zod.ts",
"gen:grammar:old": "ohm generateBundles --withTypes src/grammar/prev/*.ohm",
"gen:grammar:new": "pgen src/grammar/next/grammar.gg -o src/grammar/next/grammar.ts",
"gen:grammar": "yarn gen:grammar:old && yarn gen:grammar:new",
Expand Down Expand Up @@ -94,8 +95,8 @@
"@types/node": "^22.5.0",
"@typescript-eslint/eslint-plugin": "^8.21.0",
"@typescript-eslint/parser": "^8.21.0",
"cli-table3": "^0.6.5",
"chalk": "4.1.2",
"cli-table3": "^0.6.5",
"cross-env": "^7.0.3",
"cspell": "^8.8.3",
"diff": "^7.0.0",
Expand All @@ -110,6 +111,7 @@
"prettier": "^3.2.5",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"ts-to-zod": "^3.15.0",
"typescript": "~5.6.2"
},
"publishConfig": {
Expand Down
39 changes: 20 additions & 19 deletions src/cli/tact/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createNodeFileSystem } from "../../vfs/createNodeFileSystem";
import { createVirtualFileSystem } from "../../vfs/createVirtualFileSystem";
import { parseAndEvalExpression } from "../../optimizer/interpreter";
import { showValue } from "../../types/types";
import { Config, ConfigProject, parseConfig } from "../../config/parseConfig";
import { Config, Project, parseConfig } from "../../config/parseConfig";
import { ArgParser, GetParserResult } from "../arg-parser";
import { CliErrors } from "./error-schema";
import { CliLogger } from "../logger";
Expand Down Expand Up @@ -167,25 +167,26 @@ const parseConfigSafe = (
}
};

export const createSingleFileConfig = (fileName: string): Config => ({
projects: [
{
name: fileName,
path: ensureExtension(fileName),
output: "./",
options: {
debug: true,
external: true,
ipfsAbiGetter: false,
interfacesGetter: false,
safety: {
nullChecks: true,
export const createSingleFileConfig = (fileName: string) =>
({
projects: [
{
name: fileName,
path: ensureExtension(fileName),
output: "./",
options: {
debug: true,
external: true,
ipfsAbiGetter: false,
interfacesGetter: false,
safety: {
nullChecks: true,
},
},
mode: "full",
},
mode: "full",
},
],
});
],
}) as const;

const ensureExtension = (path: string): string => {
return path.endsWith(".tact") ? path : `${path}.tact`;
Expand Down Expand Up @@ -298,7 +299,7 @@ const filterConfig = (
};
};

type ExtraOptions = Pick<ConfigProject, "mode">;
type ExtraOptions = Pick<Project, "mode">;

const setConfigOptions = (config: Config, options: ExtraOptions): void => {
for (const project of config.projects) {
Expand Down
4 changes: 2 additions & 2 deletions src/cli/test-util.build.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { exec } from "child_process";
import { join } from "path";
import { writeFile, mkdir } from "fs/promises";
import { Config, ConfigProject } from "../config/parseConfig";
import { Config, Project } from "../config/parseConfig";

type Result = Exited | Signaled;
type Exited = { kind: "exited"; code: number; stdout: string };
Expand Down Expand Up @@ -40,7 +40,7 @@ export const makeCodegen = (outputDir: string) => {
const config = async (
name: string,
code: string,
partialConfig: Pick<ConfigProject, "options" | "mode">,
partialConfig: Pick<Project, "options" | "mode">,
) => {
await mkdir(outputDir, { recursive: true });
const outDir = outputDir;
Expand Down
128 changes: 128 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
export type SafetyOptions = {
/**
* If set to `true`, enables run-time null checks for the `!!` operator. Default is `true`.
*/
readonly nullChecks?: boolean;
};

export type ExperimentalOptions = {
/**
* If set to true, enables inlining of all functions in contracts.
* This can reduce gas usage at the cost of bigger contracts.
*/
readonly inline?: boolean;
};

/**
* Per-project configuration options
*
* Read more: https://docs.tact-lang.org/book/config#projects
*/
export type Options = {
/**
* If set to true, enables debug output of a contract and allows usage of `dump()` function,
* which is useful for debugging purposes.
*
* Read more: https://docs.tact-lang.org/book/debug
*/
readonly debug?: boolean;
/**
* If set to true, enables support of external message receivers.
*
* Read more: https://docs.tact-lang.org/book/external
*/
readonly external?: boolean;
/**
* If set to true, enables generation of a getter with IPFS links describing the contract's ABI.
*
* Read more: https://docs.tact-lang.org/ref/evolution/otp-003
*/
readonly ipfsAbiGetter?: boolean;
/**
* If set to true, enables generation of a getter with a list of interfaces provided by the contract.
*
* Read more: https://docs.tact-lang.org/book/contracts#interfaces
*/
readonly interfacesGetter?: boolean;
/**
* If set to "new", uses new parser. If set to "old", uses legacy parser. Default is "old".
*/
readonly parser?: "new" | "old";
/**
* Experimental options that might be removed in the future. Use with caution!
*/
readonly experimental?: ExperimentalOptions;
/**
* Safety options for the contract.
*/
readonly safety?: SafetyOptions;
/**
* If set to true, enables generation of `lazy_deployment_completed()` getter.
*/
readonly enableLazyDeploymentCompletedGetter?: boolean;
};

export type Mode = "fullWithDecompilation" | "full" | "funcOnly" | "checkOnly";

/**
* Per-project configuration options
*
* Read more: https://docs.tact-lang.org/book/config#projects
*/
export type Project = {
/**
* Name of the project. All generated files are prefixed with it.
*
* Read more: https://docs.tact-lang.org/book/config#projects-name
*/
name: string;
/**
* Path to the project's Tact file. You can only specify one Tact file per project.
*
* Read more: https://docs.tact-lang.org/book/config#projects-path
*/
path: string;
/**
* Path to the directory where all generated files will be placed.
*
* Read more: https://docs.tact-lang.org/book/config#projects-output
*/
output: string;
/**
* Compilation options for the project.
*
* Read more: https://docs.tact-lang.org/book/config#projects-options
*/
options?: Options;
/**
* Compilation mode of the project.
*
* Read more: https://docs.tact-lang.org/book/config#projects-mode
*/
mode?: Mode;

/**
* Set verbosity level (higher = more details), default: 1
*/
verbose?: number;
};

/**
* Compiler configuration schema
*
* Read more: https://docs.tact-lang.org/book/config
*/
export type Config = {
/**
* A property for specifying a path or URL to the JSON schema of tact.config.json
*
* Read more: https://docs.tact-lang.org/book/config#schema
*/
$schema?: string;
/**
* List of Tact projects with respective compilation options. Each .tact file represents its own Tact project.
*
* Read more: https://docs.tact-lang.org/book/config#projects
*/
projects: readonly Project[];
};
133 changes: 133 additions & 0 deletions src/config/config.zod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Generated by ts-to-zod
import { z } from "zod";
import * as C from "./config";

export const safetyOptionsSchema: z.ZodType<C.SafetyOptions> = z.object({
nullChecks: z.boolean().optional(),
});

/**
* Per-project configuration options
*
* Read more: https://docs.tact-lang.org/book/config#projects
*/
export const optionsSchema: z.ZodType<C.Options> = z.object({
/**
* If set to true, enables debug output of a contract and allows usage of `dump()` function,
* which is useful for debugging purposes.
*
* Read more: https://docs.tact-lang.org/book/debug
*/
debug: z.boolean().optional(),
/**
* If set to true, enables support of external message receivers.
*
* Read more: https://docs.tact-lang.org/book/external
*/
external: z.boolean().optional(),
/**
* If set to true, enables generation of a getter with IPFS links describing the contract's ABI.
*
* Read more: https://docs.tact-lang.org/ref/evolution/otp-003
*/
ipfsAbiGetter: z.boolean().optional(),
/**
* If set to true, enables generation of a getter with a list of interfaces provided by the contract.
*
* Read more: https://docs.tact-lang.org/book/contracts#interfaces
*/
interfacesGetter: z.boolean().optional(),
/**
* If set to "new", uses new parser. If set to "old", uses legacy parser. Default is "old".
*/
parser: z.union([z.literal("new"), z.literal("old")]).optional(),
/**
* Experimental options that might be removed in the future. Use with caution!
*/
experimental: z
.object({
/**
* If set to true, enables inlining of all functions in contracts.
* This can reduce gas usage at the cost of bigger contracts.
*/
inline: z.boolean().optional(),
})
.optional(),
/**
* Safety options for the contract.
*/
safety: safetyOptionsSchema.optional(),
/**
* If set to true, enables generation of `lazy_deployment_completed()` getter.
*/
enableLazyDeploymentCompletedGetter: z.boolean().optional(),
});

export const modeSchema: z.ZodType<C.Mode> = z.union([
z.literal("fullWithDecompilation"),
z.literal("full"),
z.literal("funcOnly"),
z.literal("checkOnly"),
]);

/**
* Per-project configuration options
*
* Read more: https://docs.tact-lang.org/book/config#projects
*/
export const projectSchema: z.ZodType<C.Project> = z.object({
/**
* Name of the project. All generated files are prefixed with it.
*
* Read more: https://docs.tact-lang.org/book/config#projects-name
*/
name: z.string(),
/**
* Path to the project's Tact file. You can only specify one Tact file per project.
*
* Read more: https://docs.tact-lang.org/book/config#projects-path
*/
path: z.string(),
/**
* Path to the directory where all generated files will be placed.
*
* Read more: https://docs.tact-lang.org/book/config#projects-output
*/
output: z.string(),
/**
* Compilation options for the project.
*
* Read more: https://docs.tact-lang.org/book/config#projects-options
*/
options: optionsSchema.optional(),
/**
* Compilation mode of the project.
*
* Read more: https://docs.tact-lang.org/book/config#projects-mode
*/
mode: modeSchema.optional(),
/**
* Set verbosity level (higher = more details), default: 1
*/
verbose: z.number().optional(),
});

/**
* Compiler configuration schema
*
* Read more: https://docs.tact-lang.org/book/config
*/
export const configSchema: z.ZodType<C.Config> = z.object({
/**
* A property for specifying a path or URL to the JSON schema of tact.config.json
*
* Read more: https://docs.tact-lang.org/book/config#schema
*/
$schema: z.string().optional(),
/**
* List of Tact projects with respective compilation options. Each .tact file represents its own Tact project.
*
* Read more: https://docs.tact-lang.org/book/config#projects
*/
projects: z.array(projectSchema),
});
Loading

0 comments on commit 93c6fba

Please sign in to comment.