@clarigen/cli 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/run-cli.js CHANGED
@@ -7,7 +7,7 @@ import { Cli, Builtins } from "clipanion";
7
7
  import { Command as Command2, Option as Option2 } from "clipanion";
8
8
 
9
9
  // src/config.ts
10
- import { z as z2 } from "zod";
10
+ import { type as type2 } from "arktype";
11
11
 
12
12
  // src/logger.ts
13
13
  import { pino } from "pino";
@@ -57,19 +57,27 @@ function sortContracts(contracts) {
57
57
  }
58
58
 
59
59
  // src/clarinet-config.ts
60
- import { z } from "zod";
60
+ import { type } from "arktype";
61
61
  import { readFile } from "fs/promises";
62
62
  import { parse } from "@iarna/toml";
63
- var ClarinetConfigSchema = z.object({
64
- project: z.object({
65
- requirements: z.array(z.object({ contract_id: z.string() })).optional(),
66
- cache_location: z.object({ path: z.string() }).optional()
63
+ var ClarinetConfig = type({
64
+ project: type({
65
+ requirements: type({
66
+ contract_id: type("string").describe("Contract ID")
67
+ }).array().describe("Project requirements").optional(),
68
+ cache_location: type({
69
+ path: type("string").describe("Cache location path")
70
+ }).optional()
67
71
  }),
68
- contracts: z.record(z.string(), z.object({ path: z.string() })).optional()
72
+ contracts: type({
73
+ "[string]": type({
74
+ path: type("string").describe("Contract path")
75
+ })
76
+ }).optional()
69
77
  });
70
78
  async function getClarinetConfig(path) {
71
79
  const file = await readFile(path, "utf-8");
72
- const config = ClarinetConfigSchema.parse(parse(file));
80
+ const config = ClarinetConfig.assert(parse(file));
73
81
  return config;
74
82
  }
75
83
 
@@ -78,23 +86,23 @@ import { dirname as dirname2, join, relative as relative2, resolve as resolve2 }
78
86
  import { stringify, parse as parse2 } from "@iarna/toml";
79
87
  import { readFile as readFile2 } from "fs/promises";
80
88
  var CONFIG_FILE = "Clarigen.toml";
81
- var typesSchema = z2.object({
82
- output: z2.string().optional(),
83
- outputs: z2.array(z2.string()).optional(),
84
- include_accounts: z2.boolean().optional(),
85
- after: z2.string().optional(),
86
- include_boot_contracts: z2.boolean().optional(),
87
- watch_folders: z2.array(z2.string()).optional()
89
+ var typesSchema = type2({
90
+ "output?": type2("string").describe("Path to the output file"),
91
+ "outputs?": type2("string[]").describe("Paths to the output files"),
92
+ "include_accounts?": type2("boolean").describe("Include accounts in the output"),
93
+ "after?": type2("string").describe("Script to run after the output is generated"),
94
+ "include_boot_contracts?": type2("boolean").describe("Include boot contracts in the output"),
95
+ "watch_folders?": type2("string[]").describe("Folders to watch for changes")
88
96
  }).optional();
89
- var ConfigFileSchema = z2.object({
90
- clarinet: z2.string(),
97
+ var ConfigFile = type2({
98
+ clarinet: type2("string").describe("Path to the Clarinet config file"),
91
99
  ["types" /* ESM */]: typesSchema,
92
100
  ["esm" /* ESM_OLD */]: typesSchema,
93
- ["docs" /* Docs */]: z2.object({
94
- output: z2.string().optional(),
95
- outputs: z2.array(z2.string()).optional(),
96
- exclude: z2.array(z2.string()).optional(),
97
- after: z2.string().optional()
101
+ ["docs" /* Docs */]: type2({
102
+ "output?": type2("string").describe("Path to docs output folder. Defaults to ./docs"),
103
+ "outputs?": type2("string[]").describe("Paths to docs output folders"),
104
+ "exclude?": type2("string[]").describe("Contracts to exclude from docs generation"),
105
+ "after?": type2("string").describe("Script to run after docs are generated")
98
106
  }).optional()
99
107
  });
100
108
  var defaultConfigFile = {
@@ -118,36 +126,36 @@ var Config = class {
118
126
  const clarinet = await getClarinetConfig(resolve2(cwd ?? "", config.clarinet));
119
127
  return new this(config, clarinet, cwd);
120
128
  }
121
- getOutputs(type) {
129
+ getOutputs(type4) {
122
130
  var _a, _b;
123
- const singlePath = (_a = this.configFile[type]) == null ? void 0 : _a.output;
124
- const multiPath = ((_b = this.configFile[type]) == null ? void 0 : _b.outputs) || [];
131
+ const singlePath = (_a = this.configFile[type4]) == null ? void 0 : _a.output;
132
+ const multiPath = ((_b = this.configFile[type4]) == null ? void 0 : _b.outputs) || [];
125
133
  if (singlePath !== void 0) return [singlePath];
126
134
  return multiPath;
127
135
  }
128
- outputResolve(type, filePath) {
129
- const outputs = this.getOutputs(type);
130
- if (!this.supports(type)) return null;
136
+ outputResolve(type4, filePath) {
137
+ const outputs = this.getOutputs(type4);
138
+ if (!this.supports(type4)) return null;
131
139
  return outputs.map((path) => {
132
140
  return resolve2(this.cwd, path, filePath || "");
133
141
  });
134
142
  }
135
- async writeOutput(type, contents, filePath) {
136
- const paths = this.outputResolve(type, filePath);
143
+ async writeOutput(type4, contents, filePath) {
144
+ const paths = this.outputResolve(type4, filePath);
137
145
  if (paths === null) return null;
138
146
  await Promise.all(
139
147
  paths.map(async (path) => {
140
148
  await writeFile(path, contents);
141
- log.debug(`Generated ${type} file at ${relative2(this.cwd, path)}`);
149
+ log.debug(`Generated ${type4} file at ${relative2(this.cwd, path)}`);
142
150
  })
143
151
  );
144
152
  return paths;
145
153
  }
146
- supports(type) {
147
- return this.getOutputs(type).length > 0;
154
+ supports(type4) {
155
+ return this.getOutputs(type4).length > 0;
148
156
  }
149
- type(type) {
150
- return this.configFile[type];
157
+ type(type4) {
158
+ return this.configFile[type4];
151
159
  }
152
160
  get esm() {
153
161
  return this.configFile["types" /* ESM */];
@@ -173,16 +181,12 @@ async function getConfig(cwd) {
173
181
  if (await fileExists(path)) {
174
182
  const toml = await readFile2(path, "utf-8");
175
183
  const parsedToml = parse2(toml);
176
- const parseResult = ConfigFileSchema.safeParse(parsedToml);
177
- if (parseResult.success) {
178
- sessionConfig = parseResult.data;
179
- } else {
180
- logger.error("Error parsing Clarigen.toml:");
181
- parseResult.error.errors.forEach((e) => {
182
- logger.error(`${e.path.join(".")}: ${e.message}`);
183
- });
184
- throw new Error("Error parsing Clarigen.toml");
184
+ const parsed = ConfigFile(parsedToml);
185
+ if (parsed instanceof type2.errors) {
186
+ logger.error(`Error parsing Clarigen config: ${parsed.summary}`);
187
+ throw new Error(`Error parsing Clarigen config: ${parsed.summary}`);
185
188
  }
189
+ sessionConfig = parsed;
186
190
  } else {
187
191
  sessionConfig = defaultConfigFile;
188
192
  }
@@ -191,7 +195,7 @@ async function getConfig(cwd) {
191
195
 
192
196
  // src/commands/base-command.ts
193
197
  import { Command, Option } from "clipanion";
194
- import { ZodError } from "zod";
198
+ import { type as type3 } from "arktype";
195
199
  var BaseCommand = class extends Command {
196
200
  verbose = Option.Boolean("-v,--verbose", false, {
197
201
  description: "Enable verbose logging"
@@ -203,8 +207,8 @@ var BaseCommand = class extends Command {
203
207
  }
204
208
  // eslint-disable-next-line @typescript-eslint/require-await
205
209
  async catch(error) {
206
- if (error instanceof ZodError) {
207
- logger.error(error.issues, "Your configuration file is invalid.");
210
+ if (error instanceof type3.errors) {
211
+ logger.error("Your configuration file is invalid.", error.summary);
208
212
  return;
209
213
  }
210
214
  logger.error(error);
@@ -291,9 +295,9 @@ var jsTypeFromAbiType = (val, isArgument = false) => {
291
295
  return `${innerType} | null`;
292
296
  } else if (isClarityAbiTuple(val)) {
293
297
  const tupleDefs = [];
294
- val.tuple.forEach(({ name, type }) => {
298
+ val.tuple.forEach(({ name, type: type4 }) => {
295
299
  const camelName = toCamelCase2(name);
296
- const innerType = jsTypeFromAbiType(type, isArgument);
300
+ const innerType = jsTypeFromAbiType(type4, isArgument);
297
301
  tupleDefs.push(`"${camelName}": ${innerType};`);
298
302
  });
299
303
  return `{
@@ -493,9 +497,9 @@ export const simnet = {
493
497
  function encodeVariables(variables) {
494
498
  return variables.map((v) => {
495
499
  let varLine = `${encodeVariableName(v.name)}: `;
496
- const type = jsTypeFromAbiType(v.type);
500
+ const type4 = jsTypeFromAbiType(v.type);
497
501
  const varJSON = serialize(v);
498
- varLine += `${varJSON} as TypedAbiVariable<${type}>`;
502
+ varLine += `${varJSON} as TypedAbiVariable<${type4}>`;
499
503
  return varLine;
500
504
  });
501
505
  }
@@ -532,6 +536,20 @@ function serializeArray(key, lines) {
532
536
  }
533
537
 
534
538
  // src/files/variables.ts
539
+ function clarityVersionForContract(contract) {
540
+ switch (contract.contract_interface.clarity_version) {
541
+ case "Clarity1":
542
+ return 1;
543
+ case "Clarity2":
544
+ return 2;
545
+ case "Clarity3":
546
+ return 3;
547
+ case "Clarity4":
548
+ return 4;
549
+ default:
550
+ return 3;
551
+ }
552
+ }
535
553
  function getVariablesV2(contract, simnet, verbose) {
536
554
  const [deployer] = contract.contract_id.split(".");
537
555
  const fakeId = `${getContractName3(contract.contract_id)}-vars`;
@@ -567,7 +585,7 @@ ${varFn}`;
567
585
  fakeId,
568
586
  fullSrc,
569
587
  {
570
- clarityVersion: 3
588
+ clarityVersion: clarityVersionForContract(contract)
571
589
  },
572
590
  deployer
573
591
  );
@@ -633,7 +651,11 @@ async function getSession(config) {
633
651
  }
634
652
  return {
635
653
  contract_id,
636
- contract_interface,
654
+ contract_interface: {
655
+ ...contract_interface,
656
+ epoch: contract_interface.epoch,
657
+ clarity_version: contract_interface.clarity_version
658
+ },
637
659
  source: source ?? ""
638
660
  };
639
661
  })
@@ -866,7 +888,7 @@ async function afterESM(config) {
866
888
 
867
889
  // src/commands/default-command.ts
868
890
  import chokidar from "chokidar";
869
- import { dirname as dirname4, join as join3, relative as relative4 } from "path";
891
+ import { dirname as dirname4, join as join3, relative as relative4 } from "node:path";
870
892
  async function generate(config) {
871
893
  const session = await getSession(config);
872
894
  const baseFile = generateBaseFile(session);
@@ -1039,8 +1061,8 @@ function clarityNameMatcher(line) {
1039
1061
  return /[\w|\-|\?|\!]+/.exec(line);
1040
1062
  }
1041
1063
  function findItemNameFromLine(line) {
1042
- const fnType = FN_TYPES.find((type) => {
1043
- return line.startsWith(`(define-${type}`);
1064
+ const fnType = FN_TYPES.find((type4) => {
1065
+ return line.startsWith(`(define-${type4}`);
1044
1066
  });
1045
1067
  if (fnType) {
1046
1068
  const prefix = `(define-${fnType} (`;
@@ -1053,13 +1075,13 @@ function findItemNameFromLine(line) {
1053
1075
  }
1054
1076
  return match[0];
1055
1077
  }
1056
- for (const type of VAR_TYPES) {
1057
- const prefix = `(define-${type} `;
1078
+ for (const type4 of VAR_TYPES) {
1079
+ const prefix = `(define-${type4} `;
1058
1080
  if (!line.startsWith(prefix)) continue;
1059
1081
  const startString = line.slice(prefix.length);
1060
1082
  const match = clarityNameMatcher(startString);
1061
1083
  if (!match) {
1062
- console.debug(`[claridocs]: Unable to determine ${type} name from line:
1084
+ console.debug(`[claridocs]: Unable to determine ${type4} name from line:
1063
1085
  \`${line}\``);
1064
1086
  return;
1065
1087
  }
@@ -1331,8 +1353,8 @@ function generateReadme(session, excluded) {
1331
1353
  contractLines.push(line);
1332
1354
  });
1333
1355
  const fileContents = `# Contracts
1334
-
1335
- ${contractLines.join("\n")}
1356
+
1357
+ ${contractLines.join("\n")}
1336
1358
  `;
1337
1359
  return fileContents;
1338
1360
  }
@@ -1396,23 +1418,81 @@ output = "docs/"
1396
1418
  }
1397
1419
 
1398
1420
  // src/commands/docs-command.ts
1421
+ import { dirname as dirname5, join as join4, relative as relative6 } from "node:path";
1422
+ import chokidar2 from "chokidar";
1423
+ async function watch2(config, cwd) {
1424
+ return new Promise(async (resolve3, reject) => {
1425
+ var _a;
1426
+ const session = await getSession(config);
1427
+ try {
1428
+ await generateDocs({
1429
+ session,
1430
+ config
1431
+ });
1432
+ } catch (error) {
1433
+ logger.error({ error }, "Error generating types");
1434
+ }
1435
+ const clarinetFolder = dirname5(config.clarinetFile());
1436
+ const contractsFolder = join4(clarinetFolder, "/contracts/**/*.clar");
1437
+ const relativeFolder = relative6(cwd || process.cwd(), contractsFolder);
1438
+ const watchFolders = ((_a = config.esm) == null ? void 0 : _a.watch_folders) ?? [];
1439
+ watchFolders.push(relativeFolder);
1440
+ logger.info(`Watching for changes in ${watchFolders}`);
1441
+ const watcher = chokidar2.watch(watchFolders, { persistent: true, cwd: clarinetFolder });
1442
+ let running = false;
1443
+ let start = 0;
1444
+ const isVerbose = logger.level !== "info";
1445
+ watcher.on("change", async (path) => {
1446
+ if (!running) {
1447
+ start = Date.now();
1448
+ logger.info(`File ${path} has been changed. Generating types.`);
1449
+ running = true;
1450
+ const session2 = await getSession(config);
1451
+ void generateDocs({
1452
+ session: session2,
1453
+ config
1454
+ }).catch((e) => {
1455
+ logger.error({ error: e }, "Error generating types");
1456
+ }).then(() => {
1457
+ setTimeout(() => {
1458
+ process.stdout.moveCursor(0, -1);
1459
+ process.stdout.clearLine(1);
1460
+ const elapsed = Date.now() - start;
1461
+ logger.info(
1462
+ `Docs generated (${(elapsed / 1e3).toFixed(2)}s). Watching for changes...`
1463
+ );
1464
+ running = false;
1465
+ });
1466
+ });
1467
+ }
1468
+ });
1469
+ });
1470
+ }
1399
1471
  var DocsCommand = class extends BaseCommand {
1400
1472
  static paths = [["docs"]];
1401
1473
  static usage = BaseCommand.Usage({
1402
1474
  description: "Generate markdown documentation for your Clarity contracts"
1403
1475
  });
1404
1476
  cwd = Option4.String({ required: false });
1477
+ watch = Option4.Boolean("-w,--watch", {
1478
+ description: "Watch for changes and regenerate docs",
1479
+ required: false
1480
+ });
1405
1481
  async execute() {
1406
1482
  this.preexecute();
1407
1483
  const config = await Config.load(this.cwd);
1408
- const session = await getSession(config);
1409
- await generateDocs({
1410
- session: {
1411
- ...session,
1412
- variables: []
1413
- },
1414
- config
1415
- });
1484
+ if (this.watch) {
1485
+ await watch2(config, this.cwd);
1486
+ } else {
1487
+ const session = await getSession(config);
1488
+ await generateDocs({
1489
+ session: {
1490
+ ...session,
1491
+ variables: []
1492
+ },
1493
+ config
1494
+ });
1495
+ }
1416
1496
  }
1417
1497
  };
1418
1498
 
@@ -1477,7 +1557,7 @@ var InitConfigCommand = class extends BaseCommand {
1477
1557
  };
1478
1558
 
1479
1559
  // src/generated/version.ts
1480
- var version = "3.1.0";
1560
+ var version = "3.2.0";
1481
1561
 
1482
1562
  // src/run-cli.ts
1483
1563
  var [node, script, ...args] = process.argv;