@codama/renderers-rust 1.2.9 → 2.0.1

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/README.md CHANGED
@@ -17,14 +17,14 @@ pnpm install @codama/renderers-rust
17
17
 
18
18
  ## Usage
19
19
 
20
- Add the following script to your Codama configuration file.
20
+ Add the following script to your Codama configuration file. The first argument is the crate folder (where `Cargo.toml` lives) and the generated code will be output to `src/generated` within that folder by default.
21
21
 
22
22
  ```json
23
23
  {
24
24
  "scripts": {
25
25
  "rust": {
26
26
  "from": "@codama/renderers-rust",
27
- "args": ["clients/rust/src/generated"]
27
+ "args": ["clients/rust"]
28
28
  }
29
29
  }
30
30
  }
@@ -36,17 +36,19 @@ An object can be passed as a second argument to further configure the renderer.
36
36
 
37
37
  The `renderVisitor` accepts the following options.
38
38
 
39
- | Name | Type | Default | Description |
40
- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
41
- | `deleteFolderBeforeRendering` | `boolean` | `true` | Whether the base directory should be cleaned before generating new files. |
42
- | `formatCode` | `boolean` | `false` | Whether we should use `cargo fmt` to format the generated code. When set to `true`, the `crateFolder` option must be provided. |
43
- | `toolchain` | `string` | `"+stable"` | The toolchain to use when formatting the generated code. |
44
- | `crateFolder` | `string` | none | The path to the root folder of the Rust crate. This option is required when `formatCode` is set to `true`. |
45
- | `linkOverrides` | `Record<'accounts' \| 'definedTypes' \| 'instructions' \| 'pdas' \| 'programs' \| 'resolvers', Record<string, string>>` | `{}` | A object that overrides the import path of link nodes. For instance, `{ definedTypes: { counter: 'hooked' } }` uses the `hooked` folder to import any link node referring to the `counter` type. |
46
- | `dependencyMap` | `Record<string, string>` | `{}` | A mapping between import aliases and their actual crate name or path in Rust. |
47
- | `renderParentInstructions` | `boolean` | `false` | When using nested instructions, whether the parent instructions should also be rendered. When set to `false` (default), only the instruction leaves are being rendered. |
48
- | `traitOptions` | [`TraitOptions`](#trait-options) | `DEFAULT_TRAIT_OPTIONS` | A set of options that can be used to configure how traits are rendered for every Rust types. See [documentation below](#trait-options) for more information. |
49
- | `anchorTraits` | `boolean` | `true` | Whether to generate Anchor traits `impl` for account types. |
39
+ | Name | Type | Default | Description |
40
+ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | `deleteFolderBeforeRendering` | `boolean` | `true` | Whether the base directory should be cleaned before generating new files. |
42
+ | `formatCode` | `boolean` | `false` | Whether we should use `cargo fmt` to format the generated code. |
43
+ | `generatedFolder` | `string` | `'src/generated'` | The path to the generated folder, relative to the crate folder provided as the first argument. |
44
+ | `toolchain` | `string` | `"+stable"` | The toolchain to use when formatting the generated code. |
45
+ | `linkOverrides` | `Record<'accounts' \| 'definedTypes' \| 'instructions' \| 'pdas' \| 'programs' \| 'resolvers', Record<string, string>>` | `{}` | A object that overrides the import path of link nodes. For instance, `{ definedTypes: { counter: 'hooked' } }` uses the `hooked` folder to import any link node referring to the `counter` type. |
46
+ | `dependencyMap` | `Record<string, string>` | `{}` | A mapping between import aliases and their actual crate name or path in Rust. |
47
+ | `dependencyVersions` | `Record<string, CargoDependency>` | `{}` | A mapping between external crates — e.g. `solana-pubkey` — and the version range we should use for them — e.g. `^3.0` or the equivalent dependency object — e.g. `{ version: "^3.0" }`. The renderer offers default values for all external dependencies it relies on but this option may be used to override some of these values or add new ones. |
48
+ | `renderParentInstructions` | `boolean` | `false` | When using nested instructions, whether the parent instructions should also be rendered. When set to `false` (default), only the instruction leaves are being rendered. |
49
+ | `traitOptions` | [`TraitOptions`](#trait-options) | `DEFAULT_TRAIT_OPTIONS` | A set of options that can be used to configure how traits are rendered for every Rust types. See [documentation below](#trait-options) for more information. |
50
+ | `anchorTraits` | `boolean` | `true` | Whether to generate Anchor traits `impl` for account types. |
51
+ | `syncCargoToml` | `boolean` | `false` | Whether to update the dependencies of the existing `Cargo.toml` — or create a new `Cargo.toml` when missing — at the crate folder provided as the first argument. |
50
52
 
51
53
  ## Trait Options
52
54
 
@@ -6,6 +6,8 @@ var errors = require('@codama/errors');
6
6
  var nodes = require('@codama/nodes');
7
7
  var renderersCore = require('@codama/renderers-core');
8
8
  var visitorsCore = require('@codama/visitors-core');
9
+ var toml = require('@iarna/toml');
10
+ var semver = require('semver');
9
11
  var codecsStrings = require('@solana/codecs-strings');
10
12
  var path = require('path');
11
13
  require('url');
@@ -17,6 +19,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
19
  var nunjucks__default = /*#__PURE__*/_interopDefault(nunjucks);
18
20
 
19
21
  // src/ImportMap.ts
22
+ var RUST_CORE_IMPORTS = /* @__PURE__ */ new Set(["std", "crate", "super", "self", "core", "alloc", "clippy"]);
20
23
  var DEFAULT_MODULE_MAP = {
21
24
  generated: "crate::generated",
22
25
  generatedAccounts: "crate::generated::accounts",
@@ -76,6 +79,10 @@ var ImportMap = class _ImportMap {
76
79
  this._aliases.forEach((alias, i) => newImportMap.addAlias(resolveDependency(i), alias));
77
80
  return newImportMap;
78
81
  }
82
+ getExternalDependencies(dependencyMap) {
83
+ const resolvedMap = this.resolveDependencyMap(dependencyMap);
84
+ return new Set([...resolvedMap._imports].map((i) => i.split("::")[0]).filter((i) => !RUST_CORE_IMPORTS.has(i)));
85
+ }
79
86
  toString(dependencies) {
80
87
  const resolvedMap = this.resolveDependencyMap(dependencies);
81
88
  const importStatements = [...resolvedMap.imports].map((i) => {
@@ -86,6 +93,254 @@ var ImportMap = class _ImportMap {
86
93
  return importStatements.join("\n");
87
94
  }
88
95
  };
96
+ var DEFAULT_DEPENDENCY_VERSIONS = {
97
+ "anchor-lang": { optional: true, version: "~0.31" },
98
+ borsh: "^0.10",
99
+ kaigan: { features: ["serde"], version: "^0.3" },
100
+ "num-derive": "^0.4",
101
+ "num-traits": "^0.2",
102
+ serde: { features: ["derive"], optional: true, version: "^1.0" },
103
+ "serde-big-array": "^0.5",
104
+ serde_with: { optional: true, version: "^3.0" },
105
+ "solana-account": "~2.2",
106
+ "solana-account-info": "~2.3",
107
+ "solana-client": { optional: true, version: "^2.2" },
108
+ "solana-cpi": "~2.2",
109
+ "solana-decode-error": "~2.3",
110
+ "solana-instruction": "~2.3",
111
+ "solana-program-error": "~2.2",
112
+ "solana-pubkey": { features: ["curve25519", "borsh"], version: "~2.3" },
113
+ "solana-sdk": { optional: true, version: "^2.3" },
114
+ thiserror: "^1.0"
115
+ };
116
+ function syncCargoToml(renderMap, crateFolder, options) {
117
+ const shouldSyncCargoToml = options.syncCargoToml ?? false;
118
+ const cargoTomlPath = renderersCore.joinPath(crateFolder, "Cargo.toml");
119
+ const usedDependencies = getUsedDependencyVersions(
120
+ renderMap,
121
+ options.dependencyMap ?? {},
122
+ options.dependencyVersions ?? {}
123
+ );
124
+ if (!shouldSyncCargoToml) {
125
+ if (renderersCore.fileExists(cargoTomlPath)) {
126
+ checkExistingCargoToml(readCargoToml(cargoTomlPath), usedDependencies);
127
+ }
128
+ return;
129
+ }
130
+ if (renderersCore.fileExists(cargoTomlPath)) {
131
+ const cargoToml = updateExistingCargoToml(readCargoToml(cargoTomlPath), usedDependencies);
132
+ renderersCore.writeFile(cargoTomlPath, toml.stringify(cargoToml) + "\n");
133
+ } else {
134
+ const cargoToml = createNewCargoToml(usedDependencies);
135
+ renderersCore.writeFile(cargoTomlPath, toml.stringify(cargoToml) + "\n");
136
+ }
137
+ }
138
+ function createNewCargoToml(usedDependencies) {
139
+ return updateExistingCargoToml(
140
+ {
141
+ name: "rust-client",
142
+ // eslint-disable-next-line sort-keys-fix/sort-keys-fix
143
+ description: "",
144
+ version: "1.0.0",
145
+ // eslint-disable-next-line sort-keys-fix/sort-keys-fix
146
+ repository: { workspace: true },
147
+ // eslint-disable-next-line sort-keys-fix/sort-keys-fix
148
+ edition: { workspace: true },
149
+ license: { workspace: true },
150
+ // eslint-disable-next-line sort-keys-fix/sort-keys-fix
151
+ features: {
152
+ anchor: ["dep:anchor-lang"],
153
+ "anchor-idl-build": ["anchor", "anchor-lang?/idl-build"],
154
+ fetch: ["dep:solana-client", "dep:solana-sdk"],
155
+ serde: ["dep:serde", "dep:serde_with"]
156
+ },
157
+ // eslint-disable-next-line sort-keys-fix/sort-keys-fix
158
+ dependencies: {}
159
+ },
160
+ usedDependencies
161
+ );
162
+ }
163
+ function updateExistingCargoToml(cargoToml, usedDependencies) {
164
+ const foundUsedDependencies = /* @__PURE__ */ new Set();
165
+ const updatedCargoToml = updateCargoDependencies(cargoToml, (dependencyGroup) => {
166
+ return Object.fromEntries(
167
+ Object.entries(dependencyGroup).map(([dependencyKey, dependency]) => {
168
+ const foundUsedDependency = findCargoDependencyByImportName(
169
+ usedDependencies,
170
+ getCargoDependencyImportName(dependencyKey)
171
+ );
172
+ if (!foundUsedDependency) {
173
+ return [dependencyKey, dependency];
174
+ }
175
+ const [usedDependencyKey, usedDependency] = foundUsedDependency;
176
+ foundUsedDependencies.add(usedDependencyKey);
177
+ const usedDependencyCrateName = getCargoDependencyCrateName(usedDependencyKey, usedDependency);
178
+ if (!shouldUpdateDependency(usedDependencyCrateName, dependency, usedDependency)) {
179
+ return [dependencyKey, dependency];
180
+ }
181
+ const newVersion = getCargoDependencyVersion(usedDependency);
182
+ return [
183
+ dependencyKey,
184
+ typeof dependency === "string" ? newVersion : { ...dependency, version: newVersion }
185
+ ];
186
+ })
187
+ );
188
+ });
189
+ const usedDependenciesToAdd = Object.entries(usedDependencies).filter(
190
+ ([usedDependencyKey]) => !foundUsedDependencies.has(usedDependencyKey)
191
+ );
192
+ for (const [usedDependencyKey, usedDependency] of usedDependenciesToAdd) {
193
+ updatedCargoToml.dependencies = updatedCargoToml.dependencies ?? {};
194
+ updatedCargoToml.dependencies[usedDependencyKey] = usedDependency;
195
+ }
196
+ return updatedCargoToml;
197
+ }
198
+ function checkExistingCargoToml(cargoToml, usedDependencies) {
199
+ const missingDependencies = [];
200
+ const dependenciesToUpdate = [];
201
+ const existingDependencies = {
202
+ ...cargoToml["build-dependencies"],
203
+ ...cargoToml["dev-dependencies"],
204
+ ...cargoToml.dependencies,
205
+ ...cargoToml.workspace?.dependencies,
206
+ ...Object.values(cargoToml.target ?? {}).reduce((acc, target) => {
207
+ return {
208
+ ...acc,
209
+ ...target["build-dependencies"],
210
+ ...target["dev-dependencies"],
211
+ ...target.dependencies
212
+ };
213
+ }, {})
214
+ };
215
+ for (const [usedDependencyKey, usedDependency] of Object.entries(usedDependencies)) {
216
+ const foundExistingDependency = findCargoDependencyByImportName(
217
+ existingDependencies,
218
+ getCargoDependencyImportName(usedDependencyKey)
219
+ );
220
+ if (!foundExistingDependency) {
221
+ missingDependencies.push(usedDependencyKey);
222
+ } else if (shouldUpdateDependency(foundExistingDependency[0], foundExistingDependency[1], usedDependency)) {
223
+ dependenciesToUpdate.push(usedDependencyKey);
224
+ }
225
+ }
226
+ if (missingDependencies.length === 0 && dependenciesToUpdate.length === 0) return;
227
+ const missingList = missingDependencies.map((d) => `- ${d} missing: ${getCargoDependencyVersion(usedDependencies[d])}
228
+ `).join("");
229
+ const outdatedList = dependenciesToUpdate.map(
230
+ (d) => `- ${d} outdated: ${getCargoDependencyVersion(existingDependencies[d])} -> ${getCargoDependencyVersion(usedDependencies[d])}
231
+ `
232
+ ).join("");
233
+ errors.logWarn(
234
+ `The following dependencies in your \`Cargo.toml\` are out-of-date or missing:
235
+ ${missingList}${outdatedList}`
236
+ );
237
+ }
238
+ function getUsedDependencyVersions(renderMap, dependencyMap, dependencyVersions) {
239
+ const usedImportNames = getUsedImportNames(renderMap, dependencyMap);
240
+ const dependencyVersionsWithDefaults = {
241
+ ...DEFAULT_DEPENDENCY_VERSIONS,
242
+ ...dependencyVersions
243
+ };
244
+ const [usedDependencyVersion, missingDependencies] = [...usedImportNames].reduce(
245
+ ([acc, missingDependencies2], usedImportName) => {
246
+ const usedDependency = findCargoDependencyByImportName(dependencyVersionsWithDefaults, usedImportName);
247
+ if (usedDependency) {
248
+ acc[usedDependency[0]] = usedDependency[1];
249
+ } else {
250
+ missingDependencies2.add(usedImportName);
251
+ }
252
+ return [acc, missingDependencies2];
253
+ },
254
+ [{}, /* @__PURE__ */ new Set()]
255
+ );
256
+ if (missingDependencies.size > 0) {
257
+ throw new errors.CodamaError(errors.CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS, {
258
+ dependencies: [...missingDependencies],
259
+ message: "Please add these dependencies to the `dependencyVersions` option."
260
+ });
261
+ }
262
+ return usedDependencyVersion;
263
+ }
264
+ function getUsedImportNames(renderMap, dependencyMap) {
265
+ const fragments = [...renderMap.values()];
266
+ const fromImportMap = new ImportMap().mergeWith(...fragments.map(({ imports }) => imports)).getExternalDependencies(dependencyMap);
267
+ const PATH_REGEX = /\b(?:::)?([a-z_][a-z0-9_]*)(?:::[a-zA-Z0-9_]+)+/g;
268
+ const fromContent = fragments.flatMap(({ content }) => {
269
+ return [...content.matchAll(PATH_REGEX)].map((match) => match[1]).filter((crateName) => !RUST_CORE_IMPORTS.has(crateName));
270
+ });
271
+ return /* @__PURE__ */ new Set([...fromImportMap, ...fromContent]);
272
+ }
273
+ function shouldUpdateDependency(dependency, currentDependency, requiredDependency) {
274
+ const currentRange = getCargoDependencyVersion(currentDependency);
275
+ const requiredRange = getCargoDependencyVersion(requiredDependency);
276
+ return !!currentRange && !!requiredRange && shouldUpdateRange(dependency, currentRange, requiredRange);
277
+ }
278
+ function shouldUpdateRange(dependency, currentRange, requiredRange) {
279
+ currentRange = cargoToNpmSemver(currentRange);
280
+ requiredRange = cargoToNpmSemver(requiredRange);
281
+ try {
282
+ if (semver.subset(currentRange, requiredRange)) {
283
+ return false;
284
+ }
285
+ const minRequiredVersion = semver.minVersion(requiredRange);
286
+ const minCurrentVersion = semver.minVersion(currentRange);
287
+ if (!minCurrentVersion || !minRequiredVersion) {
288
+ throw new Error("Could not determine minimum versions.");
289
+ }
290
+ if (semver.lt(minCurrentVersion, minRequiredVersion)) {
291
+ return true;
292
+ }
293
+ return false;
294
+ } catch (error) {
295
+ console.warn(
296
+ `Could not parse the following ranges for dependency "${dependency}": [${currentRange}] and/or [${requiredRange}]. Caused by: ${error.message}`
297
+ );
298
+ return false;
299
+ }
300
+ }
301
+ function updateCargoDependencies(cargoToml, updateFn) {
302
+ const updatedCargoToml = JSON.parse(JSON.stringify(cargoToml));
303
+ const standardSections = ["dependencies", "dev-dependencies", "build-dependencies"];
304
+ for (const section of standardSections) {
305
+ if (updatedCargoToml[section]) {
306
+ updatedCargoToml[section] = updateFn(updatedCargoToml[section]);
307
+ }
308
+ }
309
+ if (updatedCargoToml.target) {
310
+ for (const targetKey of Object.keys(updatedCargoToml.target)) {
311
+ for (const section of standardSections) {
312
+ if (updatedCargoToml.target[targetKey][section]) {
313
+ updatedCargoToml.target[targetKey][section] = updateFn(updatedCargoToml.target[targetKey][section]);
314
+ }
315
+ }
316
+ }
317
+ }
318
+ if (updatedCargoToml.workspace?.dependencies) {
319
+ updatedCargoToml.workspace.dependencies = updateFn(updatedCargoToml.workspace.dependencies);
320
+ }
321
+ return updatedCargoToml;
322
+ }
323
+ function cargoToNpmSemver(cargoVersion) {
324
+ const version = cargoVersion.trim();
325
+ return /^\d+(\.\d+)?(\.\d+)?/.test(version) ? `^${version}` : version;
326
+ }
327
+ function getCargoDependencyVersion(dependency) {
328
+ return typeof dependency === "string" ? dependency : dependency.version;
329
+ }
330
+ function getCargoDependencyCrateName(key, dependency) {
331
+ return typeof dependency !== "string" && dependency.package ? dependency.package : key;
332
+ }
333
+ function getCargoDependencyImportName(key) {
334
+ return key.replace(/-/g, "_");
335
+ }
336
+ function findCargoDependencyByImportName(dependencies, importName) {
337
+ return Object.entries(dependencies).find(([key]) => {
338
+ return getCargoDependencyImportName(key) === importName;
339
+ });
340
+ }
341
+ function readCargoToml(path) {
342
+ return toml.parse(renderersCore.readFile(path));
343
+ }
89
344
  function getBytesFromBytesValueNode(node) {
90
345
  switch (node.encoding) {
91
346
  case "utf8":
@@ -921,10 +1176,7 @@ function getRenderMapVisitor(options = {}) {
921
1176
  });
922
1177
  const hasVariableSeeds = pdaSeeds.filter(nodes.isNodeFilter("variablePdaSeedNode")).length > 0;
923
1178
  const constantSeeds = seeds.filter(nodes.isNodeFilter("constantPdaSeedNode")).filter((seed) => !nodes.isNode(seed.value, "programIdValueNode"));
924
- const { imports } = typeManifest;
925
- if (hasVariableSeeds) {
926
- imports.mergeWith(seedsImports);
927
- }
1179
+ const imports = typeManifest.imports.mergeWith(...hasVariableSeeds ? [seedsImports] : []).mergeWith(discriminatorConstants.imports).remove(`generatedAccounts::${nodes.pascalCase(node.name)}`);
928
1180
  return renderersCore.createRenderMap(`accounts/${nodes.snakeCase(node.name)}.rs`, {
929
1181
  content: render("accountsPage.njk", {
930
1182
  account: node,
@@ -932,23 +1184,25 @@ function getRenderMapVisitor(options = {}) {
932
1184
  constantSeeds,
933
1185
  discriminatorConstants: discriminatorConstants.render,
934
1186
  hasVariableSeeds,
935
- imports: imports.mergeWith(discriminatorConstants.imports).remove(`generatedAccounts::${nodes.pascalCase(node.name)}`).toString(dependencyMap),
1187
+ imports: imports.toString(dependencyMap),
936
1188
  pda,
937
1189
  program,
938
1190
  seeds,
939
1191
  typeManifest
940
- })
1192
+ }),
1193
+ imports
941
1194
  });
942
1195
  },
943
1196
  visitDefinedType(node) {
944
1197
  const typeManifest = visitorsCore.visit(node, typeManifestVisitor);
945
- const imports = new ImportMap().mergeWithManifest(typeManifest);
1198
+ const imports = new ImportMap().mergeWithManifest(typeManifest).remove(`generatedTypes::${nodes.pascalCase(node.name)}`);
946
1199
  return renderersCore.createRenderMap(`types/${nodes.snakeCase(node.name)}.rs`, {
947
1200
  content: render("definedTypesPage.njk", {
948
1201
  definedType: node,
949
- imports: imports.remove(`generatedTypes::${nodes.pascalCase(node.name)}`).toString(dependencyMap),
1202
+ imports: imports.toString(dependencyMap),
950
1203
  typeManifest
951
- })
1204
+ }),
1205
+ imports
952
1206
  });
953
1207
  },
954
1208
  visitInstruction(node) {
@@ -1009,19 +1263,20 @@ function getRenderMapVisitor(options = {}) {
1009
1263
  });
1010
1264
  const typeManifest = visitorsCore.visit(struct, structVisitor);
1011
1265
  const dataTraits = getTraitsFromNode2(node);
1012
- imports.mergeWith(dataTraits.imports);
1266
+ imports.mergeWith(dataTraits.imports).mergeWith(discriminatorConstants.imports).remove(`generatedInstructions::${nodes.pascalCase(node.name)}`);
1013
1267
  return renderersCore.createRenderMap(`instructions/${nodes.snakeCase(node.name)}.rs`, {
1014
1268
  content: render("instructionsPage.njk", {
1015
1269
  dataTraits: dataTraits.render,
1016
1270
  discriminatorConstants: discriminatorConstants.render,
1017
1271
  hasArgs,
1018
1272
  hasOptional,
1019
- imports: imports.mergeWith(discriminatorConstants.imports).remove(`generatedInstructions::${nodes.pascalCase(node.name)}`).toString(dependencyMap),
1273
+ imports: imports.toString(dependencyMap),
1020
1274
  instruction: node,
1021
1275
  instructionArgs,
1022
1276
  program,
1023
1277
  typeManifest
1024
- })
1278
+ }),
1279
+ imports
1025
1280
  });
1026
1281
  },
1027
1282
  visitProgram(node, { self }) {
@@ -1039,7 +1294,8 @@ function getRenderMapVisitor(options = {}) {
1039
1294
  errors: node.errors,
1040
1295
  imports: new ImportMap().toString(dependencyMap),
1041
1296
  program: node
1042
- })
1297
+ }),
1298
+ imports: new ImportMap()
1043
1299
  });
1044
1300
  }
1045
1301
  program = null;
@@ -1063,13 +1319,13 @@ function getRenderMapVisitor(options = {}) {
1063
1319
  };
1064
1320
  return renderersCore.mergeRenderMaps([
1065
1321
  renderersCore.createRenderMap({
1066
- ["accounts/mod.rs"]: accountsToExport.length > 0 ? { content: render("accountsMod.njk", ctx) } : void 0,
1067
- ["errors/mod.rs"]: programsToExport.length > 0 ? { content: render("errorsMod.njk", ctx) } : void 0,
1068
- ["instructions/mod.rs"]: instructionsToExport.length > 0 ? { content: render("instructionsMod.njk", ctx) } : void 0,
1069
- ["mod.rs"]: { content: render("rootMod.njk", ctx) },
1070
- ["programs.rs"]: programsToExport.length > 0 ? { content: render("programsMod.njk", ctx) } : void 0,
1071
- ["shared.rs"]: accountsToExport.length > 0 ? { content: render("sharedPage.njk", ctx) } : void 0,
1072
- ["types/mod.rs"]: definedTypesToExport.length > 0 ? { content: render("definedTypesMod.njk", ctx) } : void 0
1322
+ ["accounts/mod.rs"]: accountsToExport.length > 0 ? { content: render("accountsMod.njk", ctx), imports: new ImportMap() } : void 0,
1323
+ ["errors/mod.rs"]: programsToExport.length > 0 ? { content: render("errorsMod.njk", ctx), imports: new ImportMap() } : void 0,
1324
+ ["instructions/mod.rs"]: instructionsToExport.length > 0 ? { content: render("instructionsMod.njk", ctx), imports: new ImportMap() } : void 0,
1325
+ ["mod.rs"]: { content: render("rootMod.njk", ctx), imports: new ImportMap() },
1326
+ ["programs.rs"]: programsToExport.length > 0 ? { content: render("programsMod.njk", ctx), imports: new ImportMap() } : void 0,
1327
+ ["shared.rs"]: accountsToExport.length > 0 ? { content: render("sharedPage.njk", ctx), imports: new ImportMap() } : void 0,
1328
+ ["types/mod.rs"]: definedTypesToExport.length > 0 ? { content: render("definedTypesMod.njk", ctx), imports: new ImportMap() } : void 0
1073
1329
  }),
1074
1330
  ...nodes.getAllPrograms(node).map((p) => visitorsCore.visit(p, self))
1075
1331
  ]);
@@ -1087,24 +1343,21 @@ function getConflictsForInstructionAccountsAndArgs(instruction) {
1087
1343
  const duplicates = allNames.filter((e, i, a) => a.indexOf(e) !== i);
1088
1344
  return [...new Set(duplicates)];
1089
1345
  }
1090
- function renderVisitor(path, options = {}) {
1346
+ function renderVisitor(crateFolder, options = {}) {
1091
1347
  return visitorsCore.rootNodeVisitor((root) => {
1348
+ const generatedFolder = renderersCore.joinPath(crateFolder, options.generatedFolder ?? "src/generated");
1092
1349
  if (options.deleteFolderBeforeRendering ?? true) {
1093
- renderersCore.deleteDirectory(path);
1350
+ renderersCore.deleteDirectory(generatedFolder);
1094
1351
  }
1095
- visitorsCore.visit(root, renderersCore.writeRenderMapVisitor(getRenderMapVisitor(options), path));
1352
+ const renderMap = visitorsCore.visit(root, getRenderMapVisitor(options));
1353
+ renderersCore.writeRenderMap(renderMap, generatedFolder);
1354
+ syncCargoToml(renderMap, crateFolder, options);
1096
1355
  if (options.formatCode) {
1097
- if (options.crateFolder) {
1098
- const removeFalsy = (arg) => Boolean(arg);
1099
- runFormatter(
1100
- "cargo",
1101
- [options.toolchain, "fmt", "--manifest-path", `${options.crateFolder}/Cargo.toml`].filter(
1102
- removeFalsy
1103
- )
1104
- );
1105
- } else {
1106
- errors.logWarn("No crate folder specified, skipping formatting.");
1107
- }
1356
+ const removeFalsy = (arg) => Boolean(arg);
1357
+ runFormatter(
1358
+ "cargo",
1359
+ [options.toolchain, "fmt", "--manifest-path", `${crateFolder}/Cargo.toml`].filter(removeFalsy)
1360
+ );
1108
1361
  }
1109
1362
  });
1110
1363
  }
@@ -1123,6 +1376,7 @@ function runFormatter(cmd, args) {
1123
1376
  }
1124
1377
 
1125
1378
  exports.ImportMap = ImportMap;
1379
+ exports.RUST_CORE_IMPORTS = RUST_CORE_IMPORTS;
1126
1380
  exports.default = renderVisitor;
1127
1381
  exports.getRenderMapVisitor = getRenderMapVisitor;
1128
1382
  exports.getTypeManifestVisitor = getTypeManifestVisitor;